Goal of this post:
Sort Name column with csv filter -contains "-POS-"
Only pull back the top Bitlocker key from AzureAD and place that one key into the bitlockerKeys column.
This is a script from - https://gitlab.com/Lieben/assortedFunctions/blob/master/get-bitlockerEscrowStatusForAzureADDevices.ps1
This is not my script, but I need it to work like this for a project I am doing. Did I mention that I am a complete PS noob here? Take it easy on me please lol.
function get-bitlockerEscrowStatusForAzureADDevices{
#Requires -Modules ImportExcel
<#
.SYNOPSIS
Retrieves bitlocker key upload status for all azure ad devices
.DESCRIPTION
Use this report to determine which of your devices have backed up their bitlocker key to AzureAD (and find those that haven't and are at risk of data loss!).
Report will be stored in current folder.
.EXAMPLE
get-bitlockerEscrowStatusForAzureADDevices
.PARAMETER Credential
Optional, pass a credential object to automatically sign in to Azure AD. Global Admin permissions required
.PARAMETER showBitlockerKeysInReport
Switch, is supplied, will show the actual recovery keys in the report. Be careful where you distribute the report to if you use this
.PARAMETER showAllOSTypesInReport
By default, only the Windows OS is reported on, if for some reason you like the additional information this report gives you about devices in general, you can add this switch to show all OS types
.NOTES
filename: get-bitlockerEscrowStatusForAzureADDevices.ps1
author: Jos Lieben
blog: www.lieben.nu
created: 9/4/2019
#>
[cmdletbinding()]
Param(
$Credential,
[Switch]$showBitlockerKeysInReport,
[Switch]$showAllOSTypesInReport
)
Import-Module AzureRM.Profile
if (Get-Module -Name "AzureADPreview" -ListAvailable) {
Import-Module AzureADPreview
} elseif (Get-Module -Name "AzureAD" -ListAvailable) {
Import-Module AzureAD
}
if ($Credential) {
Try {
Connect-AzureAD -Credential $Credential -ErrorAction Stop | Out-Null
} Catch {
Write-Warning "Couldn't connect to Azure AD non-interactively, trying interactively."
Connect-AzureAD -TenantId $(($Credential.UserName.Split("#"))[1]) -ErrorAction Stop | Out-Null
}
Try {
Login-AzureRmAccount -Credential $Credential -ErrorAction Stop | Out-Null
} Catch {
Write-Warning "Couldn't connect to Azure RM non-interactively, trying interactively."
Login-AzureRmAccount -TenantId $(($Credential.UserName.Split("#"))[1]) -ErrorAction Stop | Out-Null
}
} else {
Login-AzureRmAccount -ErrorAction Stop | Out-Null
}
$context = Get-AzureRmContext
$tenantId = $context.Tenant.Id
$refreshToken = #($context.TokenCache.ReadItems() | where {$_.tenantId -eq $tenantId -and $_.ExpiresOn -gt (Get-Date)})[0].RefreshToken
$body = "grant_type=refresh_token&refresh_token=$($refreshToken)&resource=74658136-14ec-4630-ad9b-26e160ff0fc6"
$apiToken = Invoke-RestMethod "https://login.windows.net/$tenantId/oauth2/token" -Method POST -Body $body -ContentType 'application/x-www-form-urlencoded'
$restHeader = #{
'Authorization' = 'Bearer ' + $apiToken.access_token
'X-Requested-With'= 'XMLHttpRequest'
'x-ms-client-request-id'= [guid]::NewGuid()
'x-ms-correlation-id' = [guid]::NewGuid()
}
Write-Verbose "Connected, retrieving devices..."
$restResult = Invoke-RestMethod -Method GET -UseBasicParsing -Uri "https://main.iam.ad.ext.azure.com/api/Devices?nextLink=&queryParams=%7B%22searchText%22%3A%22%22%7D&top=15" -Headers $restHeader
$allDevices = #()
$allDevices += $restResult.value
while($restResult.nextLink){
$restResult = Invoke-RestMethod -Method GET -UseBasicParsing -Uri "https://main.iam.ad.ext.azure.com/api/Devices?nextLink=$([System.Web.HttpUtility]::UrlEncode($restResult.nextLink))&queryParams=%7B%22searchText%22%3A%22%22%7D&top=15" -Headers $restHeader
$allDevices += $restResult.value
}
Write-Verbose "Retrieved $($allDevices.Count) devices from AzureAD, processing information..."
$csvEntries = #()
foreach($device in $allDevices){
if(!$showAllOSTypesInReport -and $device.deviceOSType -notlike "Windows*"){
Continue
}
$keysKnownToAzure = $False
$osDriveEncrypted = $False
$lastKeyUploadDate = $Null
if($device.deviceOSType -eq "Windows" -and $device.bitLockerKey.Count -gt 0){
$keysKnownToAzure = $True
$keys = $device.bitLockerKey | Sort-Object -Property creationTime -Descending
if($keys.driveType -contains "Operating system drive"){
$osDriveEncrypted = $True
}
$lastKeyUploadDate = $keys[0].creationTime
if($showBitlockerKeysInReport){
$bitlockerKeys = ""
foreach($key in $device.bitlockerKey){
$bitlockerKeys += "$($key.creationTime)|$($key.driveType)|$($key.recoveryKey)|"
}
}else{
$bitlockerKeys = "HIDDEN FROM REPORT: READ INSTRUCTIONS TO REVEAL KEYS"
}
}else{
$bitlockerKeys = "NOT UPLOADED YET OR N/A"
}
$csvEntries += [PSCustomObject]#{"Name"=$device.displayName;"bitlockerKeys"=$bitlockerKeys}
}
$csvEntries | Export-Excel -workSheetName "BitlockerKeyReport" -path "C:\BitLockerKeyReport.xlsx" -ClearSheet -TableName "BitlockerKeyReport" -AutoSize -Verbose
}
get-bitlockerEscrowStatusForAzureADDevices -showBitlockerKeysInReport
Related
I am trying to generate a script that automatically uploads a Power BI report to a list of workspaces, but since I'm not that experienced with PowerShell I feel a bit out of my depth.
Based on this post
https://dev.to/merill/powershell-script-to-generate-a-report-on-all-power-bi-workspaces-and-groups-in-your-microsoft-365-tenant-44pc
I have written a script that generates an array of workspaces:
Connect-PowerBIServiceAccount
$workspaces = Get-PowerBIWorkspace -Scope Organization -Include All
$wslist = #()
foreach ($ws in $workspaces) {
$item = [ordered] #{
Id = $ws.ID
Name = $ws.Name
}
$u = new-object PSObject -Property $item
$wslist += $u
}
Similarly, I have written a script that generates an array of report ID's uploaded to a given Workspace:
Connect-PowerBIServiceAccount
$workspaceid = "enter-id-here"
$reports = Get-PowerBIReport -WorkspaceId $workspaceid
$rlist = #()
foreach ($r in $reports) {
$r
$item = [ordered] #{
Id = $r.ID
Name = $r.Name
}
$u = new-object PSObject -Property $item
$rlist += $u
}
What I want to do is merge these arrays into one that only includes the rows where the report name is equal to some predefined name stored in a string var.
Finally, I want to iterate through the array, and for each row delete the old report and then upload a new version.
If I got your point correctly, you need something like this:
Import-Module MicrosoftPowerBIMgmt
$oldReportName = "Fancy Report"
$pbixFilePath = "C:\Power BI\Fancy Report.pbix"
$username = "user#example.com"
$password = "P#ssw0rd" | ConvertTo-SecureString -asPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($username, $password)
Connect-PowerBIServiceAccount -Credential $credential | Out-Null
# -All - Returns all results, not only top 100
$workspaces = Get-PowerBIWorkspace -All -Scope Organization -Include All
foreach($workspace in $workspaces) {
$report = Get-PowerBIReport -WorkspaceId $workspace.Id -Scope Organization -Name $oldReportName
if ($report) {
Write-Host "Report $oldReportName found in workspace $($workspace.Name)..."
New-PowerBIReport -Path $pbixFilePath -Workspace $workspace -ConflictAction CreateOrOverwrite
}
}
Disconnect-PowerBIServiceAccount
$oldReportName is the name of the report, that you are looking for. $pbixFilePath is where the new version that should be uploaded is. I've added -All, because otherwise Get-PowerBIWorkspace will return only the first 100 workspaces in the tenant. And because this is tenant-wise operation, $username and $password should be admin credentials (as of October 2020 service principal cannot be used for admin operations).
There is no need to construct an array. Just iterate through the workspaces and check is there a report with this name published there ($report = Get-PowerBIReport -WorkspaceId $workspace.Id -Scope Organization -Name $oldReportName). If such one exists, upload the new one there and replace it (New-PowerBIReport -Path $pbixFilePath -Workspace $workspace -ConflictAction CreateOrOverwrite).
The final, fully working PS script (with dummy names and paths) looks like this:
Connect-PowerBIServiceAccount
Import-Module MicrosoftPowerBIMgmt
# Report name here:
$ReportName = "AwesomeReport"
$FolderPath = "C:\Users\AwesomeUser\AwesomeReports"
$workspaces = Get-PowerBIWorkspace -All -Scope Organization -Include All
foreach($workspace in $workspaces) {
$report = Get-PowerBIReport -WorkspaceId $workspace.Id -Scope Organization -Name $ReportName
if ($report) {
Write-Host "Report $ReportName found in workspace $($workspace.Name)..."
$filePath = "$FolderPath\$($workspace.Name)\$ReportName.pbix"
New-PowerBIReport -Path $filePath -WorkspaceId $workspace.Id -Name $ReportName -ConflictAction CreateOrOverwrite
}
}
Disconnect-PowerBIServiceAccount
I am running a script in the ISE that essentially downloads a file from a public site:
#This PowerShell code scrapes the site and downloads the latest published file.
Param(
$Url = 'https://randomwebsite.com',
$DownloadPath = "C:\Downloads",
$LocalPath = 'C:\Temp',
$RootSite = 'https://publicsite.com',
$FileExtension = '.gz'
)
#Define the session cookie used by the site and automate acceptance. $session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
$cookie = New-Object System.Net.Cookie
$cookie.Name = "name"
$cookie.Value = "True"
$cookie.Domain = "www.public.com"
$session.Cookies.Add($cookie);
$FileNameDate = Get-Date -Format yyyyMMdd
$DownloadFileName = $DownloadPath + $FileNameDate + $FileExtension
$DownloadFileName
TRY{
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$WebSite = Invoke-WebRequest $Url -WebSession $session -UseBasicParsing #this gets the links we need from the main site.
$Table = $WebSite.Links | Where-Object {$_.href -like "*FetchDocument*"} | fl href #filter the results that we need.
#Write-Output $Table
$FilterTable=($Table | Select-Object -Unique | sort href -Descending) | Out-String
$TrimString = $FilterTable.Trim()
$FinalString = $RootSite + $TrimString.Trim("href :")
#Write-Verbose $FinalString | Out-String
#Start-Process powershell.exe -verb RunAs -ArgumentList "-File C:\some\path\base_server_settings.ps1" -Wait
Invoke-WebRequest $FinalString -OutFile $DownloadFileName -TimeoutSec 600
$ExpectedFileName = Get-ChildItem | Sort-Object LastAccessTime -Descending | Select-Object -First 1 $DownloadPath.Name | SELECT Name
$ExpectedFileName
Write-Host 'The latest DLA file has been downloaded and saved here:' $DownloadFileName -ForegroundColor Green
}
CATCH{
[System.Net.WebException],[System.IO.IOException]
Write "An error occured while downloading the latest file."
Write $_.Exception.Message
}
Expectation is that it downloads a file into the downloads folder and does in fact download the file when using the ISE.
When I try to run this as a command however (PowerShell.exe -file "/path/script.ps1) I get an error stating:
An error occurred while downloading the latest file. Operation is not valid due to the current state of the object.
out-lineoutput : The object of type
"Microsoft.PowerShell.Commands.Internal.Format.GroupEndData" is not
valid or not in the correct sequence. This is likely caused by a
user-specified "format-*" command which is conflicting with the
default formatting. At
\path\to\file\AutomatedFileDownload.ps1:29
char:9
$FilterTable=($Table | Select-Object -Unique | sort href -Des ...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : InvalidData: (:) [out-lineoutput], InvalidOperationException
FullyQualifiedErrorId : ConsoleLineOutputOutOfSequencePacket,Microsoft.PowerShell.Commands.OutLineOutputCommand
I found several articles describing using the MTA or STA switch and I have tried to add in -MTA or -STA to the command, but it still gives me the same error in the command.
As commented, you are trying to get one link from the website, but pipe your commande to things like Format-List and Out-String, rendering the result to either nothing at all or as a single multiline string.. In both cases, this won't get you what you are after.
Not knowing the actual values of the linksof course, I suggest you try this:
Param(
$Url = 'https://randomwebsite.com',
$DownloadPath = "C:\Downloads",
$LocalPath = 'C:\Temp',
$RootSite = 'https://publicsite.com',
$FileExtension = '.gz'
)
# test if the download path exists and if not, create it
if (!(Test-Path -Path $DownloadPath -PathType Container)){
$null = New-Item -Path $DownloadPath -ItemType Directory
}
#Define the session cookie used by the site and automate acceptance.
$session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
$cookie = New-Object System.Net.Cookie
$cookie.Name = "name"
$cookie.Value = "True"
$cookie.Domain = "www.public.com"
$session.Cookies.Add($cookie);
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
try {
$WebSite = Invoke-WebRequest -Uri $Url -WebSession $session -UseBasicParsing -ErrorAction Stop #this gets the links we need from the main site.
# get the file link
$lastLink = ($WebSite.Links | Where-Object {$_.href -like "*FetchDocument*"} | Sort-Object href -Descending | Select-Object -First 1).href
# create the file URL
$fileUrl = "$RootSite/$lastLink"
# create the full path and filename for the downloaded file
$DownloadFileName = Join-Path -Path $DownloadPath -ChildPath ('{0:yyyyMMdd}{1}' -f (Get-Date), $FileExtension)
Write-Verbose "Downloading $fileUrl as '$DownloadFileName'"
Invoke-WebRequest -Uri $fileUrl -OutFile $DownloadFileName -TimeoutSec 600 -ErrorAction Stop
# test if the file is downloaded
if (Test-Path -Path $DownloadFileName -PathType Leaf) {
Write-Host "The latest DLA file has been downloaded and saved here: $DownloadFileName" -ForegroundColor Green
}
else {
Write-Warning "File '$DownloadFileName' has NOT been downloaded"
}
}
catch [System.Net.WebException],[System.IO.IOException]{
Write-Host "An error occured while downloading the latest file.`r`n$($_.Exception.Message)" -ForegroundColor Red
}
catch {
Write-Host "An unknown error occured while downloading the latest file.`r`n$($_.Exception.Message)" -ForegroundColor Red
}
I was asked to create a powershell script to automate the assignment for new sitecollection administrator in SharePoint-Online for BackUp purposes NetApp CloudControl.
This is my firsttime ever in PowerShell and now I got stucked and don't know where to look anymore or least don't understand what I'm looking at.
The script is supposed to do the following:
Get Microsoft-tenant and password
Create a new ps-script where the credentials are already filled in
Connect to sharepoint-online and lookup personal space for every user(onedrive for business sites)
cut the log and create a second one if more than 200 lines were written
read the log and make the service-account a sitecollectionadmin
create task to run the created script once per week
At the moment I got it to do this:
Get Microsoft-tenant and password
Save Credentials
Connect to sharepoint-online and lookup personal space for every user(onedrive for business sites)
read the log and make the service-account a sitecollectionadministrator
Can anyone of you please help me out on how to proceed with the next steps?
P.S. Please excuse that I'm posting the script as a whole, I just didn't know what I should cut out.
$TenantName = $null0
$TenantPassword = $null1
if($TenantName -eq $null0){
$TenantName = Read-Host "Enter Office 365 - Tenant Name."
$NewScript = Get-Content $PSCommandPath | ForEach-Object {$_ -replace '^\$TenantName = \$null0$',"`$TenantName = '$TenantName'"}
$NewScript | Out-File $PSCommandPath -Force
}
if($TenantPassword -eq $null1){
$TenantPassword = Read-Host "Enter Password for netapp-service#$($TenantName).onmicrosoft.com."
$NewScript = Get-Content $PSCommandPath | ForEach-Object {$_ -replace '^\$TenantPassword = \$null1$',"`$TenantPassword = '$TenantPassword'"}
$NewScript | Out-File $PSCommandPath -Force
}
$username = "netapp-service#$($TenantName).onmicrosoft.com"
$cred = New-Object -TypeName System.Management.Automation.PSCredential -argumentlist $userName, $(convertto-securestring $TenantPassword -asplaintext -force)
Connect-SPOService -Url https://$($TenantName)-admin.sharepoint.com/ -Credential $cred
$AdminURI = "https://$($TenantName)-admin.sharepoint.com"
$AdminAccount = "netapp-service#$($TenantName).onmicrosoft.com"
$AdminPass = $TenantPassword
$eDiscoveryUser = "netapp-service#$($TenantName).onmicrosoft.com"
$MySitePrefix = "https://$($TenantName)-my.sharepoint.com"
$LogFile = '.\$TenantName\$TenantName-MySites.txt'
$MySiteListFile = '.\$TenantName\$TenantName-MySites.txt'
Connect-SPOService -Url $AdminURI -Credential $cred
$loadInfo1 = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
$loadInfo2 = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")
$loadInfo3 = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.UserProfiles")
$sstr = ConvertTo-SecureString -string $AdminPass -AsPlainText –Force
$AdminPass = ""
$creds = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($AdminAccount, $sstr)
$proxyaddr = "$AdminURI/_vti_bin/UserProfileService.asmx?wsdl"
$UserProfileService= New-WebServiceProxy -Uri $proxyaddr -UseDefaultCredential False
$UserProfileService.Credentials = $creds
$strAuthCookie = $creds.GetAuthenticationCookie($AdminURI)
$uri = New-Object System.Uri($AdminURI)
$container = New-Object System.Net.CookieContainer
$container.SetCookies($uri, $strAuthCookie)
$UserProfileService.CookieContainer = $container
$UserProfileResult = $UserProfileService.GetUserProfileByIndex(-1)
Write-Host "Starting- This could take a while."
Out-File $LogFile -Force
$NumProfiles = $UserProfileService.GetUserProfileCount()
$i = 1
While ($UserProfileResult.NextValue -ne -1)
{
Write-Host "Examining profile $i of $NumProfiles"
$Prop = $UserProfileResult.UserProfile | Where-Object { $_.Name -eq "PersonalSpace" }
$Url= $Prop.Values[0].Value
if ($Url) {
$Url | Out-File $LogFile -Append -Force
}
$UserProfileResult = $UserProfileService.GetUserProfileByIndex($UserProfileResult.NextValue)
$i++
}
Write-Host "Done!"
$reader = [System.IO.File]::OpenText($MySiteListFile)
try {
for(;;) {
$line = $reader.ReadLine()
if ($line -eq $null) { break }
$fullsitepath = "$MySitePrefix$line"
Write-Host "Operating on $fullsitepath "
$fullsitepath = $fullsitepath.trimend("/")
Write-Host "Making $eDiscoveryUser a Site Collection Admin"
Set-SPOUser -Site $fullsitepath -LoginName $eDiscoveryUser -IsSiteCollectionAdmin $true
}
}
finally {
$reader.Close()
}
Disconnect-SPOService
Write-Host "Done!"
I'm trying to alter a data source for a set of Reporting Services reports, but I can't get the Powershell to work for them. I'd appreciate any help :)
$server = "http://My/ReportServer/"
$dataSource = Get-RsDataSource -Path "/Data Sources/NewDataSource" -
ReportServerUri $server
$reports = Get-RsCatalogItems -RsFolder "/Testing/NewDataSOurce" -ReportServerUri $server -Recurse | Where-Object {$_.TypeName -eq "Report"}
$reports | ForEach-Object {
$reportDataSource = Get-RsItemDataSource -RsItem $_.Path -ReportServerUri $server
$reportPath = $_.Path
if ($reportDataSource.Name -eq "OldDataSource") {
Set-RsItemDataSource -RsItem $reportPath -DataSource $dataSource -ReportServerUri $server
}
}
I wrote a function to do what you are talking about for setting data sources. Here is what I have... Unfortunately I don't have a SSRS instance any longer. The full script / module is on a gist on my GitHub account. I'll paste my gist urls at the bottom on this thread.
The function that I'm pulling the snippet out of is called Deploy-NativeSSRS. I used this module + a driver script to push items that had been checked out of TFS. So they could in turn be parsed and then pushed to SSRS during CI activities.
$reports = New-ReportObject -files (Get-ChildItem -Path $reportPath -Filter $reportExtension)
foreach($report in (($reports | Where-Object{$_.datasourcename -eq $datasourceName}).filename))
{
$fileExt = $reportExtension.trim('*')
$status = Set-SSRSDataSourceInfoNative -ReportName ($report.trim($fileext)) -reportPath $documentLibrary -DataSourceName $datasourceName -DataSourcePath "$dataSourceTarget/$datasourceName" -reportWebService $webservice
write-output "The following $report datasource was updated to $datasourcename"
}
function set-SSRSDataSourceInfoNative
{
param
(
[parameter(mandatory)]
[string]$Reportname, #with no extension SSRS has no name for the file in native mode
[parameter(mandatory)]
[string]$reportPath,
[parameter(mandatory)]
[string]$DataSourceName,
[parameter(mandatory)]
[string]$DataSourcePath,
[parameter(mandatory)]
[uri]$reportWebService,
[System.Management.Automation.PSCredential]$Credentials
)
if ($Credentials)
{$reportProxy = new-webserviceproxy -uri $reportWebService -Credential $credentials -namespace 'SSRSProxy' -class 'ReportService2010'}
else
{$reportProxy = new-webserviceproxy -uri $reportWebService -UseDefaultCredential -namespace 'SSRSProxy' -class 'ReportService2010'}
$f = $ReportName.ToLower()
try
{
$dataSources = $reportProxy.GetItemDataSources("$reportpath/$reportname")
}
catch
{
"Error was $_"
$line = $_.InvocationInfo.ScriptLineNumber
"Error was in Line $line"
"ReportName: $reportname"
"ReportPath: $reportpath"
}
$proxyNameSpace = $dataSources.gettype().Namespace
$dc = $reportProxy.GetDataSourceContents($DataSourcePath)
if ($dc)
{
$d = $dataSources | Where-Object {$_.name -like $DataSourceName }
$newDataSource = New-Object ("$proxyNameSpace.DataSource")
$newDataSource.Name = $datasourcename
$newDataSource.Item = New-Object ("$proxyNamespace.DataSourceReference")
$newDataSource.Item.Reference = $DatasourcePath
$d.item = $newDataSource.item
$reportProxy.SetItemDataSources("$reportpath/$f", $d)
$set = ($reportproxy.GetItemDataSources("$reportPath/$f")).name
write-verbose "$reportname set to data source $set"
$returnobj = 'success'
}
$returnobj
}
https://gist.github.com/crshnbrn66/40c6be436e7c2e69b4de5cd625ce0902
https://gist.github.com/crshnbrn66/b10e43ef0dadf7f4eeae620428b2cdd9
Here something that works with Power BI Report Server Rest API:
[string] $uri = "https://xxx/Reports"
$session = New-RsRestSession -ReportPortalUri $uri
$reports = Get-RsRestFolderContent -WebSession $session -RsFolder / -Recurse | Where-Object {$_.Type -eq "PowerBIReport"}
$reports | ForEach-Object {
$dataSources = Get-RsRestItemDataSource -WebSession $session -RsItem $_.Path | Where-Object {$_.ConnectionString -eq "yyy;zzz"}
#$dataSources[0].DataModelDataSource.AuthType = 'Windows'
$dataSources[0].DataModelDataSource.Username = 'domain\user'
$dataSources[0].DataModelDataSource.Secret = 'password'
Set-RsRestItemDataSource -WebSession $session -RsItem $_.Path -RsItemType 'PowerBIReport' -DataSources $dataSources
}
I'm recreating an Azure Runbook to coincide with a Logic App functionality. Long story short, I want the Logic App to initiate the Runbook, grab the results from the Runbook and use them for the next steps in the Logic App.
Initially I wanted to grab the JSON output of starting some VMs, where I ended up with the output being like this:
{
"MyVM2": true
}
{
"MyVM1": true
}
I was then going to Parse the JSON to be used in the Runbook, but soon realising that I would not have a consisted number of results (maybe 2 VMs, or 20) I had found the Parse JSON schema to only Parse what I set the schema to be (2, in my case, so anything more would be missed).
Now I figure I could put my output to a table, then use that to allow the Logic App to look inside that table to pull my VM names and success results from. So, here is the Runbook that I've been mutilating:
workflow ShutDownStartByTagasdf
{
Param(
[Parameter(Mandatory=$true)]
[String]
$TagName,
[Parameter(Mandatory=$true)]
[String]
$TagValue,
[Parameter(Mandatory=$true)]
[Boolean]
$Shutdown
)
$connectionName = "AzureRunAsConnection";
try
{
# Get the connection "AzureRunAsConnection "
$servicePrincipalConnection=Get-AutomationConnection -Name $connectionName
# Logging in to Azure
$null = Add-AzureRmAccount `
-ServicePrincipal `
-TenantId $servicePrincipalConnection.TenantId `
-ApplicationId $servicePrincipalConnection.ApplicationId `
-CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint
}
catch {
if (!$servicePrincipalConnection)
{
$ErrorMessage = "Connection $connectionName not found."
throw $ErrorMessage
} else{
Write-Error -Message $_.Exception
throw $_.Exception
}
}
$vms = Find-AzureRmResource -TagName $TagName -TagValue $TagValue | where {$_.ResourceType -like "Microsoft.Compute/virtualMachines"}
Foreach -Parallel ($vm in $vms){
if($Shutdown){
$StopRtn = Stop-AzureRmVm -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName -Force;
$objOut = [PSCustomObject]#{
VM = $vm.Name
Success = $StartRtn.IsSuccessStatusCode
}
}
else {
$StartRtn = Start-AzureRmVm -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName;
$objOut = New-Object psobject -Property #{
VM = $vm.Name
Success = $StartRtn.IsSuccessStatusCode
}
}
$outPut = InlineScript {
$Using:objOut | Format-Table Vm,Success
}
}
}
Ignore the $objOut = [PSCustomObject]#{ part, that's just history of my JSON messing about, and I'm leaving it there for now because I'm not using the $Shutdwon = $True, only $False, which is the very last part after else {
this bit:
else {
$StartRtn = Start-AzureRmVm -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName;
$objOut = New-Object psobject -Property #{
VM = $vm.Name
Success = $StartRtn.IsSuccessStatusCode
}
}
$outPut = InlineScript {
$Using:objOut | Format-Table Vm,Success
}
}
I was trying to implement something along the lines of what is already described here: Create Table with Variables in PowerShell
But there is no Output into the Azure Runbook console, but it does boot the VMs.
A very long winded explanation, but does anyone know how I could output to a Formatted Table inside a Workflow (Runbook) that will yield all my results in one table?
I ended up using an array to capture the info:
workflow ShutDownStartByTagasdf
{
Param(
[Parameter(Mandatory=$true)]
[String]
$TagName,
[Parameter(Mandatory=$true)]
[String]
$TagValue,
[Parameter(Mandatory=$true)]
[Boolean]
$Shutdown
)
$connectionName = "AzureRunAsConnection";
try
{
# Get the connection "AzureRunAsConnection "
$servicePrincipalConnection=Get-AutomationConnection -Name $connectionName
# "Logging in to Azure..."
$null = Add-AzureRmAccount `
-ServicePrincipal `
-TenantId $servicePrincipalConnection.TenantId `
-ApplicationId $servicePrincipalConnection.ApplicationId `
-CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint
}
catch {
if (!$servicePrincipalConnection)
{
$ErrorMessage = "Connection $connectionName not found."
throw $ErrorMessage
} else{
Write-Error -Message $_.Exception
throw $_.Exception
}
}
$result_array = #()
$vms = Find-AzureRmResource -TagName $TagName -TagValue $TagValue | where {$_.ResourceType -like "Microsoft.Compute/virtualMachines"}
Foreach -Parallel ($vm in $vms) {
if($Shutdown){
# Write-Output "Stopping $($vm.Name)";
$StopRtn = Stop-AzureRmVm -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName -Force;
$objOut = New-Object -TypeName psobject -Property #{
VM = $vm.Name
Success = $StopRtn.IsSuccessStatusCode
}
}
else {
# Write-Output "Starting $($vm.Name)";
$StartRtn = Start-AzureRmVm -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName;
$objOut = New-Object -TypeName psobject -Property #{
VM = $vm.Name
Success = $StartRtn.IsSuccessStatusCode
}
}
$workflow:result_array += $objOut
}
$result_array | ConvertTo-Json
}
The last bit $result_array | ConvertTo-Json allowed me to get a better output which I hope to use in the Logic App. The output:
[
{
"VM": "MyVM2",
"Success": true,
"PSComputerName": "localhost",
"PSShowComputerName": true,
"PSSourceJobInstanceId": "dadd87ad-1de1-432c-92b1-4c501c9a7ce8"
},
{
"VM": "MyVM1",
"Success": true,
"PSComputerName": "localhost",
"PSShowComputerName": true,
"PSSourceJobInstanceId": "dadd87ad-1de1-432c-92b1-4c501c9a7ce8"
}
]
I don't like how it's an array of objects, cos now I gotta figure out how to get some braces around it { } within the Logic App to utilise it better.