I have been working on the following code that supposedly retrieves all powerbi reports from the server, checks if they have refresh plans, if they dont, it outputs "No refresh pans exist..", and if it does have it, then it outputs refreshplan info like description.
$webPortalURL = "https://server-pbi.domain.com/reports"
$PBI_Reports_Array = #()
$PBI_Reports_Array = $(Invoke-RestMethod -UseDefaultCredentials -uri $($webPortalURL + "/api/v2.0/PowerBIReports"))
$loopCount = 0
$refreshPlanArray = #()
foreach ($reportPath in $PBI_Reports_Array.value.path) {
$refreshPlanArray += $(Invoke-RestMethod -UseDefaultCredentials -uri $($webPortalURL + "/api/v2.0/PowerBIReports(path='" + $reportPath + "')/CacheRefreshPlans"));
write-host "$($refreshPlanArray[$loopCount])" -foregroundcolor magenta; #testing output here to debug
if ([string]::IsNullOrEmpty($($refreshPlanArray[$loopCount].value))) {
write-host "$loopCount | $reportPath | No Refresh Plan Exists for this report!";
}
else {
write-host "$loopCount | $reportPath | $($refreshPlanArray[$loopCount].value.Description) | $($refreshPlanArray[$loopCount].value.ScheduleDescription)" -foregroundcolor magenta;
}
$loopCount++;
}
i am running into a weird bug. so i have 2 servers/portals, on one of the servers/portals, when i run this script, it retrieves all reports and does exactly what i am expecting it do as described above.
when i thought i finished developing the script, i tested it on production portal/server and it wasnt working as expected!
i debugged for many hours until i think i found whats happening, but idk what to do about it:
basically, the reason why it worked on one server/portal but not the other is because the nonproduction portal/server didnt have this error:
Invoke-RestMethod : The remote server returned an error: (404) Not Found.
apparently, before that error happened on the production server/portal where this failed, this line write-host "$($refreshPlanArray[$loopCount])" i added for debugging purposes was printing the following odata contexts up until the error happened!
#{#odata.context=https://server-pbi.domain.com/reports/api/v2.0/$metadata#CacheRefreshPlans; value=System.Object[]}
then when the error occurred after iteration 4, it stopped printing the odata!
why is that?
I figured it out!
$loopCount is the culprit! it has to be inside a try, or the "good" part of the code, otherwise, the array indexing gets messed up with the 404 NULL value
This is the correct code:
$webPortalURL = "https://server-pbi.domain.com/reports"
$PBI_Reports_Array = #()
$PBI_Reports_Array = $(Invoke-RestMethod -UseDefaultCredentials -uri $($webPortalURL + "/api/v2.0/PowerBIReports"))
$loopCount = 0
$refreshPlanArray = #()
foreach ($reportPath in $PBI_Reports_Array.value.path) {
try {
$refreshPlanArray += $(Invoke-RestMethod -UseDefaultCredentials -uri $($webPortalURL + "/api/v2.0/PowerBIReports(path='" + $reportPath + "')/CacheRefreshPlans"));
if ([string]::IsNullOrEmpty($($refreshPlanArray[$loopCount].value))) {
write-host "$loopCount | $reportPath | No Refresh Plan Exists for this report!";
}
else {
write-host "$loopCount | $reportPath | $($refreshPlanArray[$loopCount].value.Description) | $($refreshPlanArray[$loopCount].value.ScheduleDescription)" -foregroundcolor magenta;
}
$loopCount++;
}
catch {
}
}
Related
I need to collect information about total and assigned licenses programmatically.
The way that is described here: https://tech.nicolonsky.ch/manage-azure-ad-group-based-licensing-with-powershell/ - does not work on AzureUSGovernment environment. The following error occurs: ""Get-AADLicenseSku : AADSTS900382: Confidential Client is not supported in Cross Cloud request."
So, I am looking for a way to adjust it and use it on AzureUSGovernment. But I could not find the resource ID of main.iam.ad.ext.azure.us
As I understood, the ID of main.iam.ad.ext.azure.com is 74658136-14ec-4630-ad9b-26e160ff0f. But I do not understand where it is coming from.
Thank you in advance for the help.
I created a script based on the original scripts:
$context = Get-AzContext
if ($null -eq $context) {
$null = Connect-AZAccount -EA stop
$context = Get-AzContext
}
$apiToken = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id, $null, "Never", $null, "https://main.iam.ad.ext.azure.us")
$header = #{
'Authorization' = 'Bearer ' + $apiToken.AccessToken.ToString()
'Content-Type' = 'application/json'
'X-Requested-With' = 'XMLHttpRequest'
'x-ms-client-request-id' = [guid]::NewGuid()
'x-ms-correlation-id' = [guid]::NewGuid()
}
Write-Verbose "Connected to tenant: '$($context.Tenant.Id)' as: '$($context.Account)'"
$baseUrl = "https://main.iam.ad.ext.azure.us/api/"
try {
$request = Invoke-WebRequest -Method Get -Uri $($baseUrl + "AccountSkus") -Headers $header
$requestContent = $request | ConvertFrom-Json
return $requestContent
}
catch {
# convert the error message if it appears to be JSON
if ($_.ErrorDetails.Message -like "{`"Classname*") {
$local:errmsg = $_.ErrorDetails.Message | ConvertFrom-Json
if ($local:errmsg.Clientdata.operationresults.details) {
Write-Error $local:errmsg.Clientdata.operationresults.details
}
else {
Write-Error $local:errmsg
}
}
else {
Write-Error $_
}
}
But it fails with the following error:
"Invoke-RestMethod :
401 - Unauthorized: Access is denied due to invalid credentials.
Server Error
401 - Unauthorized: Access is denied due to invalid credentials.
You do not have permission to view this directory or page using the credentials that you supplied."
I tried to use the user account and service principal. Global Admin role is assigned to both.
I found the solution. In my script above ^^, I replaced
$apiToken = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id, $null, "Never", $null, "https://main.iam.ad.ext.azure.us")
with
$apiToken = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id, $null, "Never", $null, "ee62de39-b9b0-4886-aa58-08b89c4e3db3")
And now it works. Here is the example of the response:
name : Office 365 E3 - GCCHIGH
accountId : XXXXXX-XXXX-4427-8719-XXXXXXXXXXXX
accountSkuId : TEST:ENTERPRISEPACK_USGOV_GCCHIGH
availableUnits : 0
totalUnits : 1
consumedUnits : 1
skuId : aea38a85-XXXX-XXXX-aa00-XXXXXXXXXXXX
isDepartment : False
warningUnits : 0
serviceStatuses : {#{provisioningStatus=Success; servicePlan=}, #{provisioningStatus=Success; servicePlan=}, #{provisioningStatus=Success; servicePlan=}, #{provisioningStatus=Success; servicePlan=}...}
I have a script that successfully traps an application event using a SQL query in the database. If found - it will write to the event log and sent an email to support team. Now the team wants to have a double check within a four minute window. You may get ErrorA & ErrorD twice - and ErrorB & ErrorC do not reoccur.
How would you do an internal check within the 1st loop. So first time you have ErrorA.1st and second check two minutes later you see ErrorA.1st = ErrorA.2nd, therefore send off a email?
while($true) ### Endless loop - continuely looking for ERRORS to trap for two actions below (Write event log and send email)
{
$connString = "data source=sqlservername,1433;Initial catalog=HugsDB;Integrated Security=True;"
$date= $((get-date).AddSeconds(-120).ToString("MM-dd-yyyy HH:mm:ss"))
$QueryText = "select statement that graps all errors $date"
#SETUP SQL VALUES####
$SqlConnection = new-object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = $connString
$SqlCommand = $SqlConnection.CreateCommand()
$SqlCommand.CommandText = $QueryText
#### query the database
$DataAdapter = new-object System.Data.SqlClient.SqlDataAdapter $SqlCommand
$dataset = new-object System.Data.Dataset
$rowCount = $DataAdapter.Fill($dataset)
$sqlConnection.Close()
$sqlConnection.Dispose()
#### IF QUERY FINDS ERRORS # exact time of query ( could be 1 or more devices reporting an error ) write an event log message & send team an email
if($rowCount -gt 0) {
### assign a unique variable to each unique error on 1st find.
ForEach ($row in $dataset.Tables[0].Rows) {
[int]$incre = 0
$row.exciter_name = $incre.$row.exciter_name
}
## sleep 2 minutes before checking to see if we have a repeat of any of the finds in second query
Start-Sleep -Seconds 120
## Another query used to see if a reoccurance of same error.
## If the same error occurs send email and write error to event log.
if($rowCount -gt 0) {
ForEach ($row in $dataset.Tables[0].Rows) {
## NOT SURE HOW TO DO A CHECK TO LOOK FOR REOCCURANCE TO GENERATE EVENT.
write-Eventlog -LogName Exciter_Log -Source Exciter_Health –EventID 108 -Message "PACE Exciter Health Alert"
Send-MailMessage -smtpserver "$SMTPServer" -from "$EmailFrom" -to "$EmailTo" -subject "$Subject" -bodyAsHtml "$Body" -credential $anonCredentials
#################################################################################################################################################
#Second BREAK
Start-Sleep -Seconds 120
}
Sounds like you need to keep track of your previous message and compare them with the new messages. I'm assuming from the code that it does not matter which error message is actually repeated, but only that one is. In the example below what I've done is capture all unique messages in a variable called $currentErrors. Once done with capturing the messages you'll want to check if any of these previous errors appear in in this current set. If even one repeat error is found the the $alert flag is set to true and performs your error handling. Last it moves all current error messages into previous error message to be checked on the next run.
$currentErrors = #()
$alert = $false
if ($rowCount -gt 0)
{
foreach ($row in $dataset.Tables[0].Rows)
{
# not sure what these 2 lines do?
[int]$incre = 0
$row.exciter_name = $incre.$row.exciter_name
if ($currentErrors -notcontains $row.exciter_name) { $currentErrors += $row.exciter_name } # add error message to $currentErrors if not already added
}
# on first run previousErrors will be null so this foreach will do nothing
foreach ($error in $previousErrors)
{
if ($currentErrors -contains $error)
{
# previous error found in current errors
$alert = $true
break # exit loop once duplicate error is found
}
}
if ($alert)
{
Write-EventLog -LogName Exciter_Log -Source Exciter_Health –EventID 108 -Message "PACE Exciter Health Alert"
Send-MailMessage -smtpserver "$SMTPServer" -from "$EmailFrom" -to "$EmailTo" -subject "$Subject" -bodyAsHtml "$Body" -credential $anonCredentials
}
# move currentErrors into previousErrors for next loop
$previousErrors = $currentErrors
Start-Sleep -Seconds 120
}
Hi im trying to make a power shell script that can automate the enable for the data refresh schedule. can anyone help me with that?
$rs2010 = New-WebServiceProxy -Uri "URL HERE" -Namespace
SSRS.ReportingService2010 -UseDefaultCredential;
$rs2010.Timeout = 3600000
$schedules = $rs2010.ListSchedules("URL HERE");
Write-Host "--- Disabled Schedules ---";
Write-Host "----------------------------------- ";
$schedules | WHERE { $_.ScheduleStatename -ne 'Ready' }
**strong text**
i have this that can output disabled schedules. i need help to make a powershell script that can enable the data refresh whenever its turn off.
/Adel
EDIT:::
so i got this code
$rs2010 = New-WebServiceProxy -Uri
"http://url here/_vti_bin/ReportServer/ReportService2010.asmx"
-Namespace SSRS.ReportingService2010 -UseDefaultCredential;
$subscription = $rs2010.ListSubscriptions("http://url here/")
| Where-Object {$_.ScheduleStatename -ne "Ready" } ;
ForEach ($subscription in $subscriptions)
{
$rs2010.EnableDatasource($subscription.SubscriptionID);
$subscription | select subscriptionid, report, path
}
but i get this error
Exception calling "EnableDataSource" with "1" argument(s): "The path of the item 'bda17ed4-81a5-40a6-bade-894ecde02373' is not valid. The full path must be less than 260 characters long;
other restrictions apply. If the report server is in native mode, the path must start with slash. ---> Microsoft.ReportingServices.Diagnostics.Utilities.InvalidItemPathException:
My company sells/supports a product that utilizes a SQL database. I've been trying to create a PowerShell script to prep the entire server for a new install. The script needs to install all the required Windows Server Roles/features, then install SQL, then SQL Server Management Studio, and finally, Enable TCP/IP for SQL. I have gotten all but the last step to work, and trying to figure this one out is kicking my butt...
I feel like I'm on the right path here, but I'm currently stuck...
If I run:
$smo = 'Microsoft.SqlServer.Management.Smo.'
$wmi = new-object ($smo + 'Wmi.ManagedComputer')
$wmi
I actually get results showing:
ConnectionSettings :
Microsoft.SqlServer.Management.Smo.Wmi.WmiConnectionInfo
Services : {MSSQL$WEBACCESS, MSSQLFDLauncher$WEBACCESS,
SQLAgent$WEBACCESS, SQLBrowser}
ClientProtocols : {np, sm, tcp}
ServerInstances : {SQLSERVER}
ServerAliases : {}
Urn : ManagedComputer[#Name='HOSTNAME']
Name : HOSTNAME
Properties : {}
UserData :
State : Existing
I'm then using this information and running:
$uri = "ManagedComputer[#Name='']/ ServerInstance[#Name='']/ServerProtocol[#Name='Tcp']"
$Tcp = $wmi.GetSmoObject($uri)
$Tcp
With this, I get the following error:
Exception calling "GetSmoObject" with "1" argument(s): "Attempt to retrieve data for object failed for ManagedComputer 'HOSTNAME'."
At line:9 char:1
+ $Tcp = $wmi.GetSmoObject($uri)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [],
ParentContainsErrorRecordException
+ FullyQualifiedErrorId : FailedOperationException
Anybody have any idea what I'm doing wrong? I feel like, if I can figure this part out, I can figure out how to alter the settings, but I can't even pull up the settings at this point.
You should consider looking at dbatools, a PowerShell module written by SQL Server and PowerShell MVPs with hundreds of really useful functions for managing SQL Server.
I thought they might have a function that does what you need already. It looks like they don't, but in searching I had a look at Set-DbaTcpPort, and finally at the source code for that function on GitHub, where I saw this code snippet:
$wmi = New-Object Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer $instance
$wmiinstance = $wmi.ServerInstances | Where-Object { $_.Name -eq $wmiinstancename }
$tcp = $wmiinstance.ServerProtocols | Where-Object { $_.DisplayName -eq 'TCP/IP' }
$IpAddress = $tcp.IpAddresses | where-object { $_.IpAddress -eq $IpAddress }
$tcpport = $IpAddress.IpAddressProperties | Where-Object { $_.Name -eq 'TcpPort' }
So that led me to conclude that you could do the same with your object; your $wmi object seems to be the same as their $wmiinstance object even if you arrived at them slightly differently.
From there you can query with Where-Object or the .Where method:
$tcp = $wmi.ClientProtocols.Where({$_.DisplayName -eq 'TCP/IP'})
Why is the TFS Rest API for fetching the TFVC changesets returning only 256 items. I tried using the $Top to get more than 256 results. But no use.
The API for getting Git repository commits is working fine.
I am referring to https://www.visualstudio.com/en-us/docs/integrate/api/tfvc/changesets
Sample request
https://tfs.domain.com/tfs/defaultcollection/projectname/_apis/tfvc/changesets?$searchCriteria.fromDate=2016-07-12T17:49:01&$skip=0&$top=500
I tried without $skip and $searchcriteria.fromdate also. Please help me understand how to get all the results.
I managed to resolve this strange logic (thank you MSFT guys you rock :/).
In my case I want to get all changesets.
1) you need to get first 256 changesets (I used orderby in case of different order in responses):
<projectName>/_apis/tfvc/changesets?$top=256&orderby=id desc&searchCriteria.itemPath=<your_path>&api-version=1.0
2) if you have more then 0 items in response get last value of changesetNumber from response. If 0 - you got all changesets.
3) get next 256 items starting from changesetNumber:
<projectName>/_apis/tfvc/changesets?searchCriteria.toId=<changesetNumber>&$top=256&orderby=id desc&searchCriteria.itemPath=<your_path>&api-version=1.0
You need to skip first changeset (you already have this value) in response.
4) go to Step 2
So you need to replace changesetId with createdDate and searchCriteria.toId with searchCriteria.fromDate.
I hope my solution will help you.
P.S. I was unable to find any feedback on connect website.
Based on #Vitaly comments above, I've implemented the solution in posh here. ALso extracted the script below:
function Get-MaxChangeset($Project, $LastChngset)
{
$projectUri = "$rootTfsUri/$($Project.name)/_apis/tfvc/changesets?api-version=3.1&`$top=256&orderby=id desc"
$lastchangeset = $LastChngset
if($lastchangeset -ne $null){
$projectUri += "&searchCriteria.toId=$($lastchangeset.changesetId)"
}
$nestedsubresponse = Invoke-WebRequest -Uri $projectUri -UseDefaultCredentials -Method Get -Verbose -UseBasicParsing
$nestedsubresponseObject = $nestedsubresponse.Content | ConvertFrom-Json
if($nestedsubresponseObject.count -ge 1)
{
# exclude false positives.
$lastchangeset = $nestedsubresponseObject.value | `
where-object {
($_.checkedInBy.displayName -notlike "*Project Collection Service Accounts*") } | `
Sort-Object changesetId -Descending | Select-Object -First 1
if( ($lastchangeset -eq $null) -and ($nestedsubresponseObject.count -ge 256) )
{
# More records to search. Pick the bottom from current list and send it back for next batch api call.
$lastchangeset = $nestedsubresponseObject.value | Sort-Object changesetId | Select-Object -First 1
$lastchangeset = Get-MaxChangeset $Project $lastchangeset
}
}
return $lastchangeset
}
$rootTfsUri = "http://tfs:8080/tfs/DefaultCollection"
$allProjectsUri = "$rootTfsUri/_apis/projects?api-version=3.1&`$top=256"
$projectStats = #{}
$response = Invoke-WebRequest -Uri $allProjectsUri -UseDefaultCredentials -Method Get -Verbose -UseBasicParsing
$responseObject = $response.Content | ConvertFrom-Json
if($responseObject.count -ge 1)
{
foreach($prj in $responseObject.value)
{
$lastchangeset = Get-MaxChangeset $prj $null
if($lastchangeset -ne $null)
{
$projectStats.Add("$($prj.name)", $lastchangeset.createdDate)
}
else
{
$projectStats.Add("$($prj.name)", $lastchangeset)
}
}
$projectStats.GetEnumerator() | Export-Csv "CheckInHistory.csv"
}
HTH,
Sam