Compare commits

...

57 Commits

Author SHA1 Message Date
wh1te909
dd8d39e698 Release 0.6.6 2021-04-30 07:05:04 +00:00
wh1te909
afb1316daa bump versions 2021-04-30 07:01:22 +00:00
wh1te909
04d7017536 rework ping checks #444 2021-04-30 06:32:21 +00:00
wh1te909
6a1c75b060 add help toolbar #452 2021-04-30 06:01:22 +00:00
Dan
5c94611f3b Merge pull request #456 from silversword411/develop
WIP it, WIP it good: and script library stuff
2021-04-29 18:08:07 -07:00
silversword411
4e5676e80f adding the wip 2021-04-29 11:45:32 -04:00
wh1te909
c96d688a9c add alert if new trmm version available #453 2021-04-29 08:12:44 +00:00
silversword411
804242e9a5 Merge branch 'develop' of https://github.com/silversword411/tacticalrmm into develop 2021-04-28 22:50:47 -04:00
silversword411
0ec9760b17 Adding to docker 2021-04-28 22:49:49 -04:00
Dan
d481ae3da4 Merge pull request #443 from bradhawkins85/patch-16
Update Win_ScreenConnectAIO.ps1
2021-04-28 09:04:43 -07:00
silversword411
4742c14fc1 Rename temp script 2021-04-28 11:12:18 -04:00
bradhawkins85
509b0d501b Update Win_ScreenConnectAIO.ps1
Updated script notes regarding quoting around variables.
2021-04-28 10:10:18 +10:00
silversword411
d4c9b04d4e Hidden Script Library todo list 2021-04-27 13:11:30 -04:00
silversword411
16fb4d331b script library adding msi install ref script 2021-04-27 13:07:14 -04:00
silversword411
e9e5bf31a7 script library adding file copy script 2021-04-27 12:50:01 -04:00
wh1te909
221418120e Release 0.6.5 2021-04-27 16:20:25 +00:00
wh1te909
46f852e26e bump version 2021-04-27 16:20:08 +00:00
sadnub
4234cf0a31 fix policy task deletion 2021-04-27 12:12:04 -04:00
wh1te909
7f3daea648 Release 0.6.4 2021-04-27 15:36:49 +00:00
wh1te909
2eb16c82f4 bump version 2021-04-27 15:36:38 +00:00
sadnub
e00b2ce591 add test for check deletes 2021-04-27 11:04:06 -04:00
sadnub
d71e1311ca fix deleting checks 2021-04-27 10:58:23 -04:00
sadnub
2cf16963e3 fix custom fields on policy tasks 2021-04-27 10:51:29 -04:00
wh1te909
10bf7b7fb4 update restore docs 2021-04-27 06:18:15 +00:00
wh1te909
182c85a228 Release 0.6.3 2021-04-27 06:02:33 +00:00
wh1te909
94b1988b90 don't make description a required field in edit agent model 2021-04-27 06:00:42 +00:00
wh1te909
6f7e62e9a0 remove alpha status 2021-04-27 05:39:52 +00:00
wh1te909
aa7076af04 bump versions 2021-04-27 05:05:57 +00:00
Dan
c928e8f0d4 Merge pull request #436 from silversword411/develop
Updating management commands
2021-04-26 21:04:08 -07:00
sadnub
5c6b106f68 adding docs for Custom Fields, Scripting, Collector Tasks, and KeyStore 2021-04-26 23:16:10 -04:00
sadnub
d45bcea1ff add mkdocs container to docker dev env 2021-04-26 23:16:10 -04:00
wh1te909
6ff2dc79f8 black 2021-04-27 02:31:33 +00:00
silversword411
b752329987 Adding standardized header comments and example 2021-04-26 20:59:39 -04:00
silversword411
f21465335a clarifying vscode instructions 2021-04-26 20:47:23 -04:00
silversword411
0801adfc4b community script consolidating Defender status reports script 2021-04-26 17:45:56 -04:00
silversword411
5bee8052d5 Fix client site 2021-04-25 23:05:03 -04:00
silversword411
68dca5dfef Updating managment commands 2021-04-25 22:56:56 -04:00
Dan
3f51dd1d2f Merge pull request #435 from silversword411/develop
Docs and tips update
2021-04-25 00:20:05 -07:00
Dan
7f80889d77 Merge pull request #422 from sadnub/develop
Policy rework, global keystore, and collector tasks
2021-04-24 23:57:12 -07:00
sadnub
efc61c0222 fix tests 2021-04-24 22:13:02 -04:00
sadnub
6fc0a05d34 allow adding {{alert.property_name}} to resolved and failure alert scripts 2021-04-24 21:59:41 -04:00
sadnub
a9be872d7a make automated task tables sortable #431 2021-04-24 21:25:55 -04:00
sadnub
6ca85f099e fix autotask modals and allow editing the custom field for a collector task 2021-04-24 21:21:37 -04:00
sadnub
86ff677b8a fix styling 2021-04-24 21:06:17 -04:00
sadnub
35e295df86 implement keystore in script substitution with {{global.name}}. Also fixed issue with space in value. 2021-04-24 21:01:55 -04:00
sadnub
cd4d301790 keystore tests 2021-04-24 20:43:11 -04:00
sadnub
93bb329c3d add frontend end and backend for keystore 2021-04-24 20:36:21 -04:00
silversword411
7c1e0f2c30 More hidden dev docs 2021-04-24 20:06:30 -04:00
sadnub
b57f471f44 add ability to hide custom fields in UI if strictly for script usage 2021-04-24 20:01:28 -04:00
sadnub
252a9a2ed6 implement the rest of collector tasks and add tests 2021-04-24 17:40:44 -04:00
sadnub
7258d4d787 add block inheritance tests and fixes 2021-04-24 15:59:04 -04:00
sadnub
75522fa295 implement policy inheritance blocking 2021-04-24 10:07:37 -04:00
sadnub
4ba8f41d95 fixing tests 2021-04-24 10:07:37 -04:00
sadnub
f326f8e4de policy task and check rework. Added basic collector task implementation 2021-04-24 10:07:37 -04:00
sadnub
f863dc058e add UI for blocking policy inheritance on client, site, and agent 2021-04-24 10:07:37 -04:00
silversword411
20891db251 Tooltip on run interval 2021-04-24 08:52:55 -04:00
silversword411
f1d05f1342 Adding extra optional command line args to dialog 2021-04-23 16:08:16 -04:00
160 changed files with 3831 additions and 991 deletions

View File

@@ -8,7 +8,7 @@ ENV VIRTUAL_ENV ${WORKSPACE_DIR}/api/tacticalrmm/env
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
EXPOSE 8000 8383
EXPOSE 8000 8383 8005
RUN groupadd -g 1000 tactical && \
useradd -u 1000 -g 1000 tactical

View File

@@ -227,6 +227,21 @@ services:
volumes:
- tactical-data-dev:/opt/tactical
mkdocs-dev:
container_name: trmm-mkdocs-dev
image: api-dev
restart: always
build:
context: .
dockerfile: ./api.dockerfile
command: ["tactical-mkdocs-dev"]
ports:
- "8005:8005"
volumes:
- ..:/workspace:cached
networks:
- dev
volumes:
tactical-data-dev:
postgres-data-dev:

View File

@@ -170,3 +170,8 @@ if [ "$1" = 'tactical-websockets-dev' ]; then
check_tactical_ready
"${VIRTUAL_ENV}"/bin/daphne tacticalrmm.asgi:application --port 8383 -b 0.0.0.0
fi
if [ "$1" = 'tactical-mkdocs-dev' ]; then
cd "${WORKSPACE_DIR}/docs"
"${VIRTUAL_ENV}"/bin/mkdocs serve
fi

View File

@@ -11,8 +11,6 @@ It uses an [agent](https://github.com/wh1te909/rmmagent) written in golang and i
# [LIVE DEMO](https://rmm.tacticalrmm.io/)
Demo database resets every hour. Alot of features are disabled for obvious reasons due to the nature of this app.
*Tactical RMM is currently in alpha and subject to breaking changes. Use in production at your own risk.*
### [Discord Chat](https://discord.gg/upGTkWp)
### [Documentation](https://wh1te909.github.io/tacticalrmm/)

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.1.7 on 2021-04-17 01:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('agents', '0035_auto_20210329_1709'),
]
operations = [
migrations.AddField(
model_name='agent',
name='block_policy_inheritance',
field=models.BooleanField(default=False),
),
]

View File

@@ -63,6 +63,7 @@ class Agent(BaseAuditModel):
max_length=255, choices=TZ_CHOICES, null=True, blank=True
)
maintenance_mode = models.BooleanField(default=False)
block_policy_inheritance = models.BooleanField(default=False)
alert_template = models.ForeignKey(
"alerts.AlertTemplate",
related_name="agents",
@@ -96,9 +97,9 @@ class Agent(BaseAuditModel):
# or if site has changed on agent and if so generate-policies
if (
not old_agent
or old_agent
and old_agent.policy != self.policy
or old_agent.site != self.site
or (old_agent and old_agent.policy != self.policy)
or (old_agent.site != self.site)
or (old_agent.block_policy_inheritance != self.block_policy_inheritance)
):
self.generate_checks_from_policies()
self.generate_tasks_from_policies()
@@ -421,21 +422,34 @@ class Agent(BaseAuditModel):
# check site policy if agent policy doesn't have one
elif site.server_policy and site.server_policy.winupdatepolicy.exists():
patch_policy = site.server_policy.winupdatepolicy.get()
# make sure agent isn;t blocking policy inheritance
if not self.block_policy_inheritance:
patch_policy = site.server_policy.winupdatepolicy.get()
# if site doesn't have a patch policy check the client
elif (
site.client.server_policy
and site.client.server_policy.winupdatepolicy.exists()
):
patch_policy = site.client.server_policy.winupdatepolicy.get()
# make sure agent and site are not blocking inheritance
if (
not self.block_policy_inheritance
and not site.block_policy_inheritance
):
patch_policy = site.client.server_policy.winupdatepolicy.get()
# if patch policy still doesn't exist check default policy
elif (
core_settings.server_policy
and core_settings.server_policy.winupdatepolicy.exists()
):
patch_policy = core_settings.server_policy.winupdatepolicy.get()
# make sure agent site and client are not blocking inheritance
if (
not self.block_policy_inheritance
and not site.block_policy_inheritance
and not site.client.block_policy_inheritance
):
patch_policy = core_settings.server_policy.winupdatepolicy.get()
elif self.monitoring_type == "workstation":
# check agent policy first which should override client or site policy
@@ -446,21 +460,36 @@ class Agent(BaseAuditModel):
site.workstation_policy
and site.workstation_policy.winupdatepolicy.exists()
):
patch_policy = site.workstation_policy.winupdatepolicy.get()
# make sure agent isn;t blocking policy inheritance
if not self.block_policy_inheritance:
patch_policy = site.workstation_policy.winupdatepolicy.get()
# if site doesn't have a patch policy check the client
elif (
site.client.workstation_policy
and site.client.workstation_policy.winupdatepolicy.exists()
):
patch_policy = site.client.workstation_policy.winupdatepolicy.get()
# make sure agent and site are not blocking inheritance
if (
not self.block_policy_inheritance
and not site.block_policy_inheritance
):
patch_policy = site.client.workstation_policy.winupdatepolicy.get()
# if patch policy still doesn't exist check default policy
elif (
core_settings.workstation_policy
and core_settings.workstation_policy.winupdatepolicy.exists()
):
patch_policy = core_settings.workstation_policy.winupdatepolicy.get()
# make sure agent site and client are not blocking inheritance
if (
not self.block_policy_inheritance
and not site.block_policy_inheritance
and not site.client.block_policy_inheritance
):
patch_policy = (
core_settings.workstation_policy.winupdatepolicy.get()
)
# if policy still doesn't exist return the agent patch policy
if not patch_policy:
@@ -527,6 +556,7 @@ class Agent(BaseAuditModel):
and site.server_policy
and site.server_policy.alert_template
and site.server_policy.alert_template.is_active
and not self.block_policy_inheritance
):
templates.append(site.server_policy.alert_template)
if (
@@ -534,6 +564,7 @@ class Agent(BaseAuditModel):
and site.workstation_policy
and site.workstation_policy.alert_template
and site.workstation_policy.alert_template.is_active
and not self.block_policy_inheritance
):
templates.append(site.workstation_policy.alert_template)
@@ -547,6 +578,8 @@ class Agent(BaseAuditModel):
and client.server_policy
and client.server_policy.alert_template
and client.server_policy.alert_template.is_active
and not self.block_policy_inheritance
and not site.block_policy_inheritance
):
templates.append(client.server_policy.alert_template)
if (
@@ -554,15 +587,28 @@ class Agent(BaseAuditModel):
and client.workstation_policy
and client.workstation_policy.alert_template
and client.workstation_policy.alert_template.is_active
and not self.block_policy_inheritance
and not site.block_policy_inheritance
):
templates.append(client.workstation_policy.alert_template)
# check if alert template is on client and return
if client.alert_template and client.alert_template.is_active:
if (
client.alert_template
and client.alert_template.is_active
and not self.block_policy_inheritance
and not site.block_policy_inheritance
):
templates.append(client.alert_template)
# check if alert template is applied globally and return
if core.alert_template and core.alert_template.is_active:
if (
core.alert_template
and core.alert_template.is_active
and not self.block_policy_inheritance
and not site.block_policy_inheritance
and not client.block_policy_inheritance
):
templates.append(core.alert_template)
# if agent is a workstation, check if policy with alert template is assigned to the site, client, or core
@@ -571,6 +617,9 @@ class Agent(BaseAuditModel):
and core.server_policy
and core.server_policy.alert_template
and core.server_policy.alert_template.is_active
and not self.block_policy_inheritance
and not site.block_policy_inheritance
and not client.block_policy_inheritance
):
templates.append(core.server_policy.alert_template)
if (
@@ -578,6 +627,9 @@ class Agent(BaseAuditModel):
and core.workstation_policy
and core.workstation_policy.alert_template
and core.workstation_policy.alert_template.is_active
and not self.block_policy_inheritance
and not site.block_policy_inheritance
and not client.block_policy_inheritance
):
templates.append(core.workstation_policy.alert_template)
@@ -731,36 +783,6 @@ class Agent(BaseAuditModel):
except:
pass
# define how the agent should handle pending actions
def handle_pending_actions(self):
pending_actions = self.pendingactions.filter(status="pending") # type: ignore
for action in pending_actions:
if action.action_type == "taskaction":
from autotasks.tasks import (
create_win_task_schedule,
delete_win_task_schedule,
enable_or_disable_win_task,
)
task_id = action.details["task_id"]
if action.details["action"] == "taskcreate":
create_win_task_schedule.delay(task_id, pending_action=action.id)
elif action.details["action"] == "tasktoggle":
enable_or_disable_win_task.delay(
task_id, action.details["value"], pending_action=action.id
)
elif action.details["action"] == "taskdelete":
delete_win_task_schedule.delay(task_id, pending_action=action.id)
# for clearing duplicate pending actions on agent
def remove_matching_pending_task_actions(self, task_id):
# remove any other pending actions on agent with same task_id
for action in self.pendingactions.filter(action_type="taskaction").exclude(status="completed"): # type: ignore
if action.details["task_id"] == task_id:
action.delete()
def should_create_alert(self, alert_template=None):
return (
self.overdue_dashboard_alert

View File

@@ -116,6 +116,7 @@ class AgentTableSerializer(serializers.ModelSerializer):
"logged_username",
"italic",
"policy",
"block_policy_inheritance",
]
depth = 2

View File

@@ -1,5 +1,6 @@
from __future__ import annotations
import re
from typing import TYPE_CHECKING, Union
from django.conf import settings
@@ -297,7 +298,7 @@ class Alert(models.Model):
if alert_template and alert_template.action and not alert.action_run:
r = agent.run_script(
scriptpk=alert_template.action.pk,
args=alert_template.action_args,
args=alert.parse_script_args(alert_template.action_args),
timeout=alert_template.action_timeout,
wait=True,
full=True,
@@ -406,7 +407,7 @@ class Alert(models.Model):
):
r = agent.run_script(
scriptpk=alert_template.resolved_action.pk,
args=alert_template.resolved_action_args,
args=alert.parse_script_args(alert_template.resolved_action_args),
timeout=alert_template.resolved_action_timeout,
wait=True,
full=True,
@@ -428,6 +429,36 @@ class Alert(models.Model):
f"Resolved action: {alert_template.action.name} failed to run on any agent for {agent.hostname} resolved alert"
)
def parse_script_args(self, args: list[str]):
if not args:
return []
temp_args = list()
# pattern to match for injection
pattern = re.compile(".*\\{\\{alert\\.(.*)\\}\\}.*")
for arg in args:
match = pattern.match(arg)
if match:
name = match.group(1)
if hasattr(self, name):
value = getattr(self, name)
else:
continue
try:
temp_args.append(re.sub("\\{\\{.*\\}\\}", "'" + value + "'", arg)) # type: ignore
except Exception as e:
logger.error(e)
continue
else:
temp_args.append(arg)
return temp_args
class AlertTemplate(models.Model):
name = models.CharField(max_length=100)

View File

@@ -5,6 +5,7 @@ from unittest.mock import patch
from django.conf import settings
from django.utils import timezone as djangotime
from model_bakery import baker
from autotasks.models import AutomatedTask
from tacticalrmm.test import TacticalTestCase
@@ -203,3 +204,138 @@ class TestAPIv3(TacticalTestCase):
self.assertEqual(r.status_code, 200)
self.assertEqual(r.json(), {"mode": "rpc", "shellcmd": ""})
reload_nats.assert_called_once()
def test_task_runner_get(self):
from autotasks.serializers import TaskGOGetSerializer
r = self.client.get("/api/v3/500/asdf9df9dfdf/taskrunner/")
self.assertEqual(r.status_code, 404)
# setup data
agent = baker.make_recipe("agents.agent")
task = baker.make("autotasks.AutomatedTask", agent=agent)
url = f"/api/v3/{task.pk}/{agent.agent_id}/taskrunner/" # type: ignore
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertEqual(TaskGOGetSerializer(task).data, r.data) # type: ignore
def test_task_runner_results(self):
from agents.models import AgentCustomField
r = self.client.patch("/api/v3/500/asdf9df9dfdf/taskrunner/")
self.assertEqual(r.status_code, 404)
# setup data
agent = baker.make_recipe("agents.agent")
task = baker.make("autotasks.AutomatedTask", agent=agent)
url = f"/api/v3/{task.pk}/{agent.agent_id}/taskrunner/" # type: ignore
# test passing task
data = {
"stdout": "test test \ntestest stdgsd\n",
"stderr": "",
"retcode": 0,
"execution_time": 3.560,
}
r = self.client.patch(url, data)
self.assertEqual(r.status_code, 200)
self.assertTrue(AutomatedTask.objects.get(pk=task.pk).status == "passing") # type: ignore
# test failing task
data = {
"stdout": "test test \ntestest stdgsd\n",
"stderr": "",
"retcode": 1,
"execution_time": 3.560,
}
r = self.client.patch(url, data)
self.assertEqual(r.status_code, 200)
self.assertTrue(AutomatedTask.objects.get(pk=task.pk).status == "failing") # type: ignore
# test collector task
text = baker.make("core.CustomField", model="agent", type="text", name="Test")
boolean = baker.make(
"core.CustomField", model="agent", type="checkbox", name="Test1"
)
multiple = baker.make(
"core.CustomField", model="agent", type="multiple", name="Test2"
)
# test text fields
task.custom_field = text # type: ignore
task.save() # type: ignore
# test failing failing with stderr
data = {
"stdout": "test test \nthe last line",
"stderr": "This is an error",
"retcode": 1,
"execution_time": 3.560,
}
r = self.client.patch(url, data)
self.assertEqual(r.status_code, 200)
self.assertTrue(AutomatedTask.objects.get(pk=task.pk).status == "failing") # type: ignore
# test saving to text field
data = {
"stdout": "test test \nthe last line",
"stderr": "",
"retcode": 0,
"execution_time": 3.560,
}
r = self.client.patch(url, data)
self.assertEqual(r.status_code, 200)
self.assertEqual(AutomatedTask.objects.get(pk=task.pk).status, "passing") # type: ignore
self.assertEqual(AgentCustomField.objects.get(field=text, agent=task.agent).value, "the last line") # type: ignore
# test saving to checkbox field
task.custom_field = boolean # type: ignore
task.save() # type: ignore
data = {
"stdout": "1",
"stderr": "",
"retcode": 0,
"execution_time": 3.560,
}
r = self.client.patch(url, data)
self.assertEqual(r.status_code, 200)
self.assertEqual(AutomatedTask.objects.get(pk=task.pk).status, "passing") # type: ignore
self.assertTrue(AgentCustomField.objects.get(field=boolean, agent=task.agent).value) # type: ignore
# test saving to multiple field with commas
task.custom_field = multiple # type: ignore
task.save() # type: ignore
data = {
"stdout": "this,is,an,array",
"stderr": "",
"retcode": 0,
"execution_time": 3.560,
}
r = self.client.patch(url, data)
self.assertEqual(r.status_code, 200)
self.assertEqual(AutomatedTask.objects.get(pk=task.pk).status, "passing") # type: ignore
self.assertEqual(AgentCustomField.objects.get(field=multiple, agent=task.agent).value, ["this", "is", "an", "array"]) # type: ignore
# test mutiple with a single value
data = {
"stdout": "this",
"stderr": "",
"retcode": 0,
"execution_time": 3.560,
}
r = self.client.patch(url, data)
self.assertEqual(r.status_code, 200)
self.assertEqual(AutomatedTask.objects.get(pk=task.pk).status, "passing") # type: ignore
self.assertEqual(AgentCustomField.objects.get(field=multiple, agent=task.agent).value, ["this"]) # type: ignore

View File

@@ -15,7 +15,7 @@ from rest_framework.response import Response
from rest_framework.views import APIView
from accounts.models import User
from agents.models import Agent
from agents.models import Agent, AgentCustomField
from agents.serializers import WinAgentSerializer
from autotasks.models import AutomatedTask
from autotasks.serializers import TaskGOGetSerializer, TaskRunnerPatchSerializer
@@ -65,9 +65,17 @@ class CheckIn(APIView):
if Alert.objects.filter(agent=agent, resolved=False).exists():
Alert.handle_alert_resolve(agent)
# get any pending actions
if agent.pendingactions.filter(status="pending").exists(): # type: ignore
agent.handle_pending_actions()
# sync scheduled tasks
if agent.autotasks.exclude(sync_status="synced").exists(): # type: ignore
tasks = agent.autotasks.exclude(sync_status="synced") # type: ignore
for task in tasks:
if task.sync_status == "pendingdeletion":
task.delete_task_on_agent()
elif task.sync_status == "initial":
task.modify_task_on_agent()
elif task.sync_status == "notsynced":
task.create_task_on_agent()
return Response("ok")
@@ -351,11 +359,42 @@ class TaskRunner(APIView):
instance=task, data=request.data, partial=True
)
serializer.is_valid(raise_exception=True)
serializer.save(last_run=djangotime.now())
new_task = serializer.save(last_run=djangotime.now())
status = "failing" if task.retcode != 0 else "passing"
# check if task is a collector and update the custom field
if task.custom_field:
if not task.stderr:
if AgentCustomField.objects.filter(
field=task.custom_field, agent=task.agent
).exists():
agent_field = AgentCustomField.objects.get(
field=task.custom_field, agent=task.agent
)
else:
agent_field = AgentCustomField.objects.create(
field=task.custom_field, agent=task.agent
)
# get last line of stdout
value = new_task.stdout.split("\n")[-1].strip()
if task.custom_field.type in ["text", "number", "single", "datetime"]:
agent_field.string_value = value
agent_field.save()
elif task.custom_field.type == "multiple":
agent_field.multiple_value = value.split(",")
agent_field.save()
elif task.custom_field.type == "checkbox":
agent_field.bool_value = bool(value)
agent_field.save()
status = "passing"
else:
status = "failing"
else:
status = "failing" if task.retcode != 0 else "passing"
new_task: AutomatedTask = AutomatedTask.objects.get(pk=task.pk)
new_task.status = status
new_task.save()
@@ -393,7 +432,7 @@ class SysInfo(APIView):
class MeshExe(APIView):
""" Sends the mesh exe to the installer """
"""Sends the mesh exe to the installer"""
def post(self, request):
exe = "meshagent.exe" if request.data["arch"] == "64" else "meshagent-x86.exe"

View File

@@ -1,7 +1,6 @@
from django.db import models
from agents.models import Agent
from core.models import CoreSettings
from django.db import models
from logs.models import BaseAuditModel
@@ -29,7 +28,8 @@ class Policy(BaseAuditModel):
def save(self, *args, **kwargs):
from alerts.tasks import cache_agents_alert_template
from automation.tasks import generate_agent_checks_from_policies_task
from automation.tasks import generate_agent_checks_task
# get old policy if exists
old_policy = type(self).objects.get(pk=self.pk) if self.pk else None
@@ -38,8 +38,8 @@ class Policy(BaseAuditModel):
# generate agent checks only if active and enforced were changed
if old_policy:
if old_policy.active != self.active or old_policy.enforced != self.enforced:
generate_agent_checks_from_policies_task.delay(
policypk=self.pk,
generate_agent_checks_task.delay(
policy=self.pk,
create_tasks=True,
)
@@ -52,7 +52,10 @@ class Policy(BaseAuditModel):
agents = list(self.related_agents().only("pk").values_list("pk", flat=True))
super(BaseAuditModel, self).delete(*args, **kwargs)
generate_agent_checks_task.delay(agents, create_tasks=True)
generate_agent_checks_task.delay(agents=agents, create_tasks=True)
def __str__(self):
return self.name
@property
def is_default_server_policy(self):
@@ -62,9 +65,6 @@ class Policy(BaseAuditModel):
def is_default_workstation_policy(self):
return self.default_workstation_policy.exists() # type: ignore
def __str__(self):
return self.name
def is_agent_excluded(self, agent):
return (
agent in self.excluded_agents.all()
@@ -94,20 +94,29 @@ class Policy(BaseAuditModel):
filtered_agents_pks = Policy.objects.none()
filtered_agents_pks |= Agent.objects.filter(
site__in=[
site
for site in explicit_sites
if site.client not in explicit_clients
and site.client not in self.excluded_clients.all()
],
monitoring_type=mon_type,
).values_list("pk", flat=True)
filtered_agents_pks |= (
Agent.objects.exclude(block_policy_inheritance=True)
.filter(
site__in=[
site
for site in explicit_sites
if site.client not in explicit_clients
and site.client not in self.excluded_clients.all()
],
monitoring_type=mon_type,
)
.values_list("pk", flat=True)
)
filtered_agents_pks |= Agent.objects.filter(
site__client__in=[client for client in explicit_clients],
monitoring_type=mon_type,
).values_list("pk", flat=True)
filtered_agents_pks |= (
Agent.objects.exclude(block_policy_inheritance=True)
.exclude(site__block_policy_inheritance=True)
.filter(
site__client__in=[client for client in explicit_clients],
monitoring_type=mon_type,
)
.values_list("pk", flat=True)
)
return Agent.objects.filter(
models.Q(pk__in=filtered_agents_pks)
@@ -123,9 +132,6 @@ class Policy(BaseAuditModel):
@staticmethod
def cascade_policy_tasks(agent):
from autotasks.models import AutomatedTask
from autotasks.tasks import delete_win_task_schedule
from logs.models import PendingAction
# List of all tasks to be applied
tasks = list()
@@ -154,6 +160,17 @@ class Policy(BaseAuditModel):
client_policy = client.workstation_policy
site_policy = site.workstation_policy
# check if client/site/agent is blocking inheritance and blank out policies
if agent.block_policy_inheritance:
site_policy = None
client_policy = None
default_policy = None
elif site.block_policy_inheritance:
client_policy = None
default_policy = None
elif client.block_policy_inheritance:
default_policy = None
if (
agent_policy
and agent_policy.active
@@ -200,26 +217,16 @@ class Policy(BaseAuditModel):
if taskpk not in added_task_pks
]
):
delete_win_task_schedule.delay(task.pk)
if task.sync_status == "initial":
task.delete()
else:
task.sync_status = "pendingdeletion"
task.save()
# handle matching tasks that haven't synced to agent yet or pending deletion due to agent being offline
for action in agent.pendingactions.filter(action_type="taskaction").exclude(
status="completed"
):
task = AutomatedTask.objects.get(pk=action.details["task_id"])
if (
task.parent_task in agent_tasks_parent_pks
and task.parent_task in added_task_pks
):
agent.remove_matching_pending_task_actions(task.id)
PendingAction(
agent=agent,
action_type="taskaction",
details={"action": "taskcreate", "task_id": task.id},
).save()
task.sync_status = "notsynced"
task.save(update_fields=["sync_status"])
# change tasks from pendingdeletion to notsynced if policy was added or changed
agent.autotasks.filter(sync_status="pendingdeletion").filter(
parent_task__in=[taskpk for taskpk in added_task_pks]
).update(sync_status="notsynced")
return [task for task in tasks if task.pk not in agent_tasks_parent_pks]
@@ -251,6 +258,17 @@ class Policy(BaseAuditModel):
client_policy = client.workstation_policy
site_policy = site.workstation_policy
# check if client/site/agent is blocking inheritance and blank out policies
if agent.block_policy_inheritance:
site_policy = None
client_policy = None
default_policy = None
elif site.block_policy_inheritance:
client_policy = None
default_policy = None
elif client.block_policy_inheritance:
default_policy = None
# Used to hold the policies that will be applied and the order in which they are applied
# Enforced policies are applied first
enforced_checks = list()

View File

@@ -1,169 +1,143 @@
from agents.models import Agent
from automation.models import Policy
from autotasks.models import AutomatedTask
from checks.models import Check
from typing import Any, Dict, List, Union
from tacticalrmm.celery import app
@app.task
# generates policy checks on agents affected by a policy and optionally generate automated tasks
def generate_agent_checks_from_policies_task(policypk, create_tasks=False):
def generate_agent_checks_task(
policy: int = None,
site: int = None,
client: int = None,
agents: List[int] = list(),
all: bool = False,
create_tasks: bool = False,
) -> Union[str, None]:
from agents.models import Agent
policy = Policy.objects.get(pk=policypk)
from automation.models import Policy
if policy.is_default_server_policy and policy.is_default_workstation_policy:
agents = Agent.objects.prefetch_related("policy").only("pk", "monitoring_type")
elif policy.is_default_server_policy:
agents = Agent.objects.filter(monitoring_type="server").only(
"pk", "monitoring_type"
)
elif policy.is_default_workstation_policy:
agents = Agent.objects.filter(monitoring_type="workstation").only(
p = Policy.objects.get(pk=policy) if policy else None
# generate checks on all agents if all is specified or if policy is default server/workstation policy
if (p and p.is_default_server_policy and p.is_default_workstation_policy) or all:
a = Agent.objects.prefetch_related("policy").only("pk", "monitoring_type")
# generate checks on all servers if policy is a default servers policy
elif p and p.is_default_server_policy:
a = Agent.objects.filter(monitoring_type="server").only("pk", "monitoring_type")
# generate checks on all workstations if policy is a default workstations policy
elif p and p.is_default_workstation_policy:
a = Agent.objects.filter(monitoring_type="workstation").only(
"pk", "monitoring_type"
)
# generate checks on a list of supplied agents
elif agents:
a = Agent.objects.filter(pk__in=agents)
# generate checks on agents affected by supplied policy
elif policy:
a = p.related_agents().only("pk")
# generate checks that has specified site
elif site:
a = Agent.objects.filter(site_id=site)
# generate checks that has specified client
elif client:
a = Agent.objects.filter(site__client_id=client)
else:
agents = policy.related_agents().only("pk")
a = []
for agent in agents:
for agent in a:
agent.generate_checks_from_policies()
if create_tasks:
agent.generate_tasks_from_policies()
@app.task
# generates policy checks on a list of agents and optionally generate automated tasks
def generate_agent_checks_task(agentpks, create_tasks=False):
for agent in Agent.objects.filter(pk__in=agentpks):
agent.generate_checks_from_policies()
if create_tasks:
agent.generate_tasks_from_policies()
@app.task
# generates policy checks on agent servers or workstations within a certain client or site and optionally generate automated tasks
def generate_agent_checks_by_location_task(location, mon_type, create_tasks=False):
for agent in Agent.objects.filter(**location).filter(monitoring_type=mon_type):
agent.generate_checks_from_policies()
if create_tasks:
agent.generate_tasks_from_policies()
@app.task
# generates policy checks on all agent servers or workstations and optionally generate automated tasks
def generate_all_agent_checks_task(mon_type, create_tasks=False):
for agent in Agent.objects.filter(monitoring_type=mon_type):
agent.generate_checks_from_policies()
if create_tasks:
agent.generate_tasks_from_policies()
@app.task
# deletes a policy managed check from all agents
def delete_policy_check_task(checkpk):
Check.objects.filter(parent_check=checkpk).delete()
return "ok"
@app.task
# updates policy managed check fields on agents
def update_policy_check_fields_task(checkpk):
def update_policy_check_fields_task(check: int) -> str:
from checks.models import Check
check = Check.objects.get(pk=checkpk)
c: Check = Check.objects.get(pk=check)
update_fields: Dict[Any, Any] = {}
Check.objects.filter(parent_check=checkpk).update(
warning_threshold=check.warning_threshold,
error_threshold=check.error_threshold,
alert_severity=check.alert_severity,
name=check.name,
run_interval=check.run_interval,
disk=check.disk,
fails_b4_alert=check.fails_b4_alert,
ip=check.ip,
script=check.script,
script_args=check.script_args,
info_return_codes=check.info_return_codes,
warning_return_codes=check.warning_return_codes,
timeout=check.timeout,
pass_if_start_pending=check.pass_if_start_pending,
pass_if_svc_not_exist=check.pass_if_svc_not_exist,
restart_if_stopped=check.restart_if_stopped,
log_name=check.log_name,
event_id=check.event_id,
event_id_is_wildcard=check.event_id_is_wildcard,
event_type=check.event_type,
event_source=check.event_source,
event_message=check.event_message,
fail_when=check.fail_when,
search_last_days=check.search_last_days,
number_of_events_b4_alert=check.number_of_events_b4_alert,
email_alert=check.email_alert,
text_alert=check.text_alert,
dashboard_alert=check.dashboard_alert,
)
for field in c.policy_fields_to_copy:
update_fields[field] = getattr(c, field)
Check.objects.filter(parent_check=check).update(**update_fields)
return "ok"
@app.task
# generates policy tasks on agents affected by a policy
def generate_agent_tasks_from_policies_task(policypk):
def generate_agent_autotasks_task(policy: int = None) -> str:
from agents.models import Agent
policy = Policy.objects.get(pk=policypk)
from automation.models import Policy
if policy.is_default_server_policy and policy.is_default_workstation_policy:
p: Policy = Policy.objects.get(pk=policy)
if p and p.is_default_server_policy and p.is_default_workstation_policy:
agents = Agent.objects.prefetch_related("policy").only("pk", "monitoring_type")
elif policy.is_default_server_policy:
elif p and p.is_default_server_policy:
agents = Agent.objects.filter(monitoring_type="server").only(
"pk", "monitoring_type"
)
elif policy.is_default_workstation_policy:
elif p and p.is_default_workstation_policy:
agents = Agent.objects.filter(monitoring_type="workstation").only(
"pk", "monitoring_type"
)
else:
agents = policy.related_agents().only("pk")
agents = p.related_agents().only("pk")
for agent in agents:
agent.generate_tasks_from_policies()
return "ok"
@app.task
def delete_policy_autotask_task(taskpk):
def delete_policy_autotasks_task(task: int) -> str:
from autotasks.models import AutomatedTask
from autotasks.tasks import delete_win_task_schedule
for task in AutomatedTask.objects.filter(parent_task=taskpk):
delete_win_task_schedule.delay(task.pk)
for t in AutomatedTask.objects.filter(parent_task=task):
t.delete_task_on_agent()
return "ok"
@app.task
def run_win_policy_autotask_task(task_pks):
from autotasks.tasks import run_win_task
def run_win_policy_autotasks_task(task: int) -> str:
from autotasks.models import AutomatedTask
for task in task_pks:
run_win_task.delay(task)
for t in AutomatedTask.objects.filter(parent_task=task):
t.run_win_task()
return "ok"
@app.task
def update_policy_task_fields_task(taskpk, update_agent=False):
from autotasks.tasks import enable_or_disable_win_task
def update_policy_autotasks_fields_task(task: int, update_agent: bool = False) -> str:
from autotasks.models import AutomatedTask
task = AutomatedTask.objects.get(pk=taskpk)
t = AutomatedTask.objects.get(pk=task)
update_fields: Dict[str, Any] = {}
AutomatedTask.objects.filter(parent_task=taskpk).update(
alert_severity=task.alert_severity,
email_alert=task.email_alert,
text_alert=task.text_alert,
dashboard_alert=task.dashboard_alert,
script=task.script,
script_args=task.script_args,
name=task.name,
timeout=task.timeout,
enabled=task.enabled,
)
for field in t.policy_fields_to_copy:
update_fields[field] = getattr(t, field)
AutomatedTask.objects.filter(parent_task=task).update(**update_fields)
if update_agent:
for task in AutomatedTask.objects.filter(parent_task=taskpk):
enable_or_disable_win_task.delay(task.pk, task.enabled)
for t in AutomatedTask.objects.filter(parent_task=task).exclude(
sync_status="initial"
):
t.modify_task_on_agent()
return "ok"

View File

@@ -52,7 +52,8 @@ class TestPolicyViews(TacticalTestCase):
self.check_not_authenticated("get", url)
def test_add_policy(self):
@patch("autotasks.models.AutomatedTask.create_task_on_agent")
def test_add_policy(self, create_task):
url = "/automation/policies/"
data = {
@@ -90,8 +91,8 @@ class TestPolicyViews(TacticalTestCase):
self.check_not_authenticated("post", url)
@patch("automation.tasks.generate_agent_checks_from_policies_task.delay")
def test_update_policy(self, generate_agent_checks_from_policies_task):
@patch("automation.tasks.generate_agent_checks_task.delay")
def test_update_policy(self, generate_agent_checks_task):
# returns 404 for invalid policy pk
resp = self.client.put("/automation/policies/500/", format="json")
self.assertEqual(resp.status_code, 404)
@@ -110,7 +111,7 @@ class TestPolicyViews(TacticalTestCase):
self.assertEqual(resp.status_code, 200)
# only called if active or enforced are updated
generate_agent_checks_from_policies_task.assert_not_called()
generate_agent_checks_task.assert_not_called()
data = {
"name": "Test Policy Update",
@@ -121,8 +122,8 @@ class TestPolicyViews(TacticalTestCase):
resp = self.client.put(url, data, format="json")
self.assertEqual(resp.status_code, 200)
generate_agent_checks_from_policies_task.assert_called_with(
policypk=policy.pk, create_tasks=True # type: ignore
generate_agent_checks_task.assert_called_with(
policy=policy.pk, create_tasks=True # type: ignore
)
self.check_not_authenticated("put", url)
@@ -145,7 +146,7 @@ class TestPolicyViews(TacticalTestCase):
self.assertEqual(resp.status_code, 200)
generate_agent_checks_task.assert_called_with(
[agent.pk for agent in agents], create_tasks=True
agents=[agent.pk for agent in agents], create_tasks=True
)
self.check_not_authenticated("delete", url)
@@ -271,7 +272,7 @@ class TestPolicyViews(TacticalTestCase):
self.check_not_authenticated("patch", url)
@patch("automation.tasks.run_win_policy_autotask_task.delay")
@patch("automation.tasks.run_win_policy_autotasks_task.delay")
def test_run_win_task(self, mock_task):
# create managed policy tasks
@@ -281,11 +282,12 @@ class TestPolicyViews(TacticalTestCase):
parent_task=1,
_quantity=6,
)
url = "/automation/runwintask/1/"
resp = self.client.put(url, format="json")
self.assertEqual(resp.status_code, 200)
mock_task.assert_called_once_with([task.pk for task in tasks]) # type: ignore
mock_task.assert_called() # type: ignore
self.check_not_authenticated("put", url)
@@ -426,7 +428,7 @@ class TestPolicyViews(TacticalTestCase):
self.check_not_authenticated("delete", url)
@patch("automation.tasks.generate_agent_checks_from_policies_task.delay")
@patch("automation.tasks.generate_agent_checks_task.delay")
def test_sync_policy(self, generate_checks):
url = "/automation/sync/"
@@ -441,7 +443,7 @@ class TestPolicyViews(TacticalTestCase):
resp = self.client.post(url, data, format="json")
self.assertEqual(resp.status_code, 200)
generate_checks.assert_called_with(policy.pk, create_tasks=True) # type: ignore
generate_checks.assert_called_with(policy=policy.pk, create_tasks=True) # type: ignore
self.check_not_authenticated("post", url)
@@ -497,7 +499,7 @@ class TestPolicyTasks(TacticalTestCase):
self.assertEquals(len(resp.data["agents"]), 10) # type: ignore
def test_generating_agent_policy_checks(self):
from .tasks import generate_agent_checks_from_policies_task
from .tasks import generate_agent_checks_task
# setup data
policy = baker.make("automation.Policy", active=True)
@@ -505,7 +507,7 @@ class TestPolicyTasks(TacticalTestCase):
agent = baker.make_recipe("agents.agent", policy=policy)
# test policy assigned to agent
generate_agent_checks_from_policies_task(policy.id) # type: ignore
generate_agent_checks_task(policy=policy.id) # type: ignore
# make sure all checks were created. should be 7
agent_checks = Agent.objects.get(pk=agent.id).agentchecks.all()
@@ -545,7 +547,7 @@ class TestPolicyTasks(TacticalTestCase):
self.assertEqual(check.event_type, checks[6].event_type)
def test_generating_agent_policy_checks_with_enforced(self):
from .tasks import generate_agent_checks_from_policies_task
from .tasks import generate_agent_checks_task
# setup data
policy = baker.make("automation.Policy", active=True, enforced=True)
@@ -555,7 +557,7 @@ class TestPolicyTasks(TacticalTestCase):
agent = baker.make_recipe("agents.agent", site=site, policy=policy)
self.create_checks(agent=agent, script=script)
generate_agent_checks_from_policies_task(policy.id, create_tasks=True) # type: ignore
generate_agent_checks_task(policy=policy.id, create_tasks=True) # type: ignore
# make sure each agent check says overriden_by_policy
self.assertEqual(Agent.objects.get(pk=agent.id).agentchecks.count(), 14)
@@ -566,13 +568,12 @@ class TestPolicyTasks(TacticalTestCase):
7,
)
@patch("automation.tasks.generate_agent_checks_by_location_task.delay")
@patch("autotasks.models.AutomatedTask.create_task_on_agent")
@patch("automation.tasks.generate_agent_checks_task.delay")
def test_generating_agent_policy_checks_by_location(
self, generate_agent_checks_by_location_task
self, generate_agent_checks_mock, create_task
):
from automation.tasks import (
generate_agent_checks_by_location_task as generate_agent_checks,
)
from automation.tasks import generate_agent_checks_task
# setup data
policy = baker.make("automation.Policy", active=True)
@@ -596,16 +597,14 @@ class TestPolicyTasks(TacticalTestCase):
workstation_agent.client.save()
# should trigger task in save method on core
generate_agent_checks_by_location_task.assert_called_with(
location={"site__client_id": workstation_agent.client.pk},
mon_type="workstation",
generate_agent_checks_mock.assert_called_with(
client=workstation_agent.client.pk,
create_tasks=True,
)
generate_agent_checks_by_location_task.reset_mock()
generate_agent_checks_mock.reset_mock()
generate_agent_checks(
location={"site__client_id": workstation_agent.client.pk},
mon_type="workstation",
generate_agent_checks_task(
client=workstation_agent.client.pk,
create_tasks=True,
)
@@ -620,16 +619,14 @@ class TestPolicyTasks(TacticalTestCase):
workstation_agent.client.save()
# should trigger task in save method on core
generate_agent_checks_by_location_task.assert_called_with(
location={"site__client_id": workstation_agent.client.pk},
mon_type="workstation",
generate_agent_checks_mock.assert_called_with(
client=workstation_agent.client.pk,
create_tasks=True,
)
generate_agent_checks_by_location_task.reset_mock()
generate_agent_checks_mock.reset_mock()
generate_agent_checks(
location={"site__client_id": workstation_agent.client.pk},
mon_type="workstation",
generate_agent_checks_task(
client=workstation_agent.client.pk,
create_tasks=True,
)
@@ -644,16 +641,14 @@ class TestPolicyTasks(TacticalTestCase):
server_agent.client.save()
# should trigger task in save method on core
generate_agent_checks_by_location_task.assert_called_with(
location={"site__client_id": server_agent.client.pk},
mon_type="server",
generate_agent_checks_mock.assert_called_with(
client=server_agent.client.pk,
create_tasks=True,
)
generate_agent_checks_by_location_task.reset_mock()
generate_agent_checks_mock.reset_mock()
generate_agent_checks(
location={"site__client_id": server_agent.client.pk},
mon_type="server",
generate_agent_checks_task(
client=server_agent.client.pk,
create_tasks=True,
)
@@ -668,16 +663,14 @@ class TestPolicyTasks(TacticalTestCase):
server_agent.client.save()
# should trigger task in save method on core
generate_agent_checks_by_location_task.assert_called_with(
location={"site__client_id": server_agent.client.pk},
mon_type="server",
generate_agent_checks_mock.assert_called_with(
client=server_agent.client.pk,
create_tasks=True,
)
generate_agent_checks_by_location_task.reset_mock()
generate_agent_checks_mock.reset_mock()
generate_agent_checks(
location={"site__client_id": server_agent.client.pk},
mon_type="server",
generate_agent_checks_task(
client=server_agent.client.pk,
create_tasks=True,
)
@@ -692,16 +685,14 @@ class TestPolicyTasks(TacticalTestCase):
workstation_agent.site.save()
# should trigger task in save method on core
generate_agent_checks_by_location_task.assert_called_with(
location={"site_id": workstation_agent.site.pk},
mon_type="workstation",
generate_agent_checks_mock.assert_called_with(
site=workstation_agent.site.pk,
create_tasks=True,
)
generate_agent_checks_by_location_task.reset_mock()
generate_agent_checks_mock.reset_mock()
generate_agent_checks(
location={"site_id": workstation_agent.site.pk},
mon_type="workstation",
generate_agent_checks_task(
site=workstation_agent.site.pk,
create_tasks=True,
)
@@ -716,16 +707,14 @@ class TestPolicyTasks(TacticalTestCase):
workstation_agent.site.save()
# should trigger task in save method on core
generate_agent_checks_by_location_task.assert_called_with(
location={"site_id": workstation_agent.site.pk},
mon_type="workstation",
generate_agent_checks_mock.assert_called_with(
site=workstation_agent.site.pk,
create_tasks=True,
)
generate_agent_checks_by_location_task.reset_mock()
generate_agent_checks_mock.reset_mock()
generate_agent_checks(
location={"site_id": workstation_agent.site.pk},
mon_type="workstation",
generate_agent_checks_task(
site=workstation_agent.site.pk,
create_tasks=True,
)
@@ -740,16 +729,14 @@ class TestPolicyTasks(TacticalTestCase):
server_agent.site.save()
# should trigger task in save method on core
generate_agent_checks_by_location_task.assert_called_with(
location={"site_id": server_agent.site.pk},
mon_type="server",
generate_agent_checks_mock.assert_called_with(
site=server_agent.site.pk,
create_tasks=True,
)
generate_agent_checks_by_location_task.reset_mock()
generate_agent_checks_mock.reset_mock()
generate_agent_checks(
location={"site_id": server_agent.site.pk},
mon_type="server",
generate_agent_checks_task(
site=server_agent.site.pk,
create_tasks=True,
)
@@ -764,16 +751,14 @@ class TestPolicyTasks(TacticalTestCase):
server_agent.site.save()
# should trigger task in save method on core
generate_agent_checks_by_location_task.assert_called_with(
location={"site_id": server_agent.site.pk},
mon_type="server",
generate_agent_checks_mock.assert_called_with(
site=server_agent.site.pk,
create_tasks=True,
)
generate_agent_checks_by_location_task.reset_mock()
generate_agent_checks_mock.reset_mock()
generate_agent_checks(
location={"site_id": server_agent.site.pk},
mon_type="server",
generate_agent_checks_task(
site=server_agent.site.pk,
create_tasks=True,
)
@@ -783,13 +768,10 @@ class TestPolicyTasks(TacticalTestCase):
Agent.objects.get(pk=workstation_agent.id).agentchecks.count(), 0
)
@patch("automation.tasks.generate_all_agent_checks_task.delay")
def test_generating_policy_checks_for_all_agents(
self, generate_all_agent_checks_task
):
@patch("automation.tasks.generate_agent_checks_task.delay")
def test_generating_policy_checks_for_all_agents(self, generate_agent_checks_mock):
from core.models import CoreSettings
from .tasks import generate_all_agent_checks_task as generate_all_checks
from .tasks import generate_agent_checks_task
# setup data
policy = baker.make("automation.Policy", active=True)
@@ -801,11 +783,9 @@ class TestPolicyTasks(TacticalTestCase):
core.server_policy = policy
core.save()
generate_all_agent_checks_task.assert_called_with(
mon_type="server", create_tasks=True
)
generate_all_agent_checks_task.reset_mock()
generate_all_checks(mon_type="server", create_tasks=True)
generate_agent_checks_mock.assert_called_with(all=True, create_tasks=True)
generate_agent_checks_mock.reset_mock()
generate_agent_checks_task(all=True, create_tasks=True)
# all servers should have 7 checks
for agent in server_agents:
@@ -818,15 +798,9 @@ class TestPolicyTasks(TacticalTestCase):
core.workstation_policy = policy
core.save()
generate_all_agent_checks_task.assert_any_call(
mon_type="workstation", create_tasks=True
)
generate_all_agent_checks_task.assert_any_call(
mon_type="server", create_tasks=True
)
generate_all_agent_checks_task.reset_mock()
generate_all_checks(mon_type="server", create_tasks=True)
generate_all_checks(mon_type="workstation", create_tasks=True)
generate_agent_checks_mock.assert_any_call(all=True, create_tasks=True)
generate_agent_checks_mock.reset_mock()
generate_agent_checks_task(all=True, create_tasks=True)
# all workstations should have 7 checks
for agent in server_agents:
@@ -838,11 +812,9 @@ class TestPolicyTasks(TacticalTestCase):
core.workstation_policy = None
core.save()
generate_all_agent_checks_task.assert_called_with(
mon_type="workstation", create_tasks=True
)
generate_all_agent_checks_task.reset_mock()
generate_all_checks(mon_type="workstation", create_tasks=True)
generate_agent_checks_mock.assert_called_with(all=True, create_tasks=True)
generate_agent_checks_mock.reset_mock()
generate_agent_checks_task(all=True, create_tasks=True)
# nothing should have the checks
for agent in server_agents:
@@ -851,31 +823,8 @@ class TestPolicyTasks(TacticalTestCase):
for agent in workstation_agents:
self.assertEqual(Agent.objects.get(pk=agent.id).agentchecks.count(), 0)
def test_delete_policy_check(self):
from .models import Policy
from .tasks import delete_policy_check_task
policy = baker.make("automation.Policy", active=True)
self.create_checks(policy=policy)
agent = baker.make_recipe("agents.server_agent", policy=policy)
# make sure agent has 7 checks
self.assertEqual(Agent.objects.get(pk=agent.id).agentchecks.count(), 7)
# pick a policy check and delete it from the agent
policy_check_id = Policy.objects.get(pk=policy.id).policychecks.first().id # type: ignore
delete_policy_check_task(policy_check_id)
# make sure policy check doesn't exist on agent
self.assertEqual(Agent.objects.get(pk=agent.id).agentchecks.count(), 6)
self.assertFalse(
Agent.objects.get(pk=agent.id)
.agentchecks.filter(parent_check=policy_check_id)
.exists()
)
def update_policy_check_fields(self):
@patch("autotasks.models.AutomatedTask.create_task_on_agent")
def update_policy_check_fields(self, create_task):
from .models import Policy
from .tasks import update_policy_check_fields_task
@@ -905,8 +854,9 @@ class TestPolicyTasks(TacticalTestCase):
"12.12.12.12",
)
def test_generate_agent_tasks(self):
from .tasks import generate_agent_tasks_from_policies_task
@patch("autotasks.models.AutomatedTask.create_task_on_agent")
def test_generate_agent_tasks(self, create_task):
from .tasks import generate_agent_autotasks_task
# create test data
policy = baker.make("automation.Policy", active=True)
@@ -915,7 +865,7 @@ class TestPolicyTasks(TacticalTestCase):
)
agent = baker.make_recipe("agents.server_agent", policy=policy)
generate_agent_tasks_from_policies_task(policy.id) # type: ignore
generate_agent_autotasks_task(policy=policy.id) # type: ignore
agent_tasks = Agent.objects.get(pk=agent.id).autotasks.all()
@@ -934,56 +884,61 @@ class TestPolicyTasks(TacticalTestCase):
self.assertEqual(task.parent_task, tasks[2].id) # type: ignore
self.assertEqual(task.name, tasks[2].name) # type: ignore
@patch("autotasks.tasks.delete_win_task_schedule.delay")
def test_delete_policy_tasks(self, delete_win_task_schedule):
from .tasks import delete_policy_autotask_task
@patch("autotasks.models.AutomatedTask.create_task_on_agent")
@patch("autotasks.models.AutomatedTask.delete_task_on_agent")
def test_delete_policy_tasks(self, delete_task_on_agent, create_task):
from .tasks import delete_policy_autotasks_task
policy = baker.make("automation.Policy", active=True)
tasks = baker.make("autotasks.AutomatedTask", policy=policy, _quantity=3)
agent = baker.make_recipe("agents.server_agent", policy=policy)
baker.make_recipe("agents.server_agent", policy=policy)
delete_policy_autotask_task(tasks[0].id) # type: ignore
delete_policy_autotasks_task(task=tasks[0].id) # type: ignore
delete_win_task_schedule.assert_called_with(
agent.autotasks.get(parent_task=tasks[0].id).id # type: ignore
)
delete_task_on_agent.assert_called()
@patch("autotasks.tasks.run_win_task.delay")
def test_run_policy_task(self, run_win_task):
from .tasks import run_win_policy_autotask_task
@patch("autotasks.models.AutomatedTask.create_task_on_agent")
@patch("autotasks.models.AutomatedTask.run_win_task")
def test_run_policy_task(self, run_win_task, create_task):
from .tasks import run_win_policy_autotasks_task
tasks = baker.make("autotasks.AutomatedTask", _quantity=3)
policy = baker.make("automation.Policy", active=True)
tasks = baker.make("autotasks.AutomatedTask", policy=policy, _quantity=3)
baker.make_recipe("agents.server_agent", policy=policy)
run_win_policy_autotask_task([task.id for task in tasks]) # type: ignore
run_win_policy_autotasks_task(task=tasks[0].id) # type: ignore
run_win_task.side_effect = [task.id for task in tasks] # type: ignore
self.assertEqual(run_win_task.call_count, 3)
for task in tasks: # type: ignore
run_win_task.assert_any_call(task.id) # type: ignore
run_win_task.assert_called_once()
@patch("autotasks.tasks.enable_or_disable_win_task.delay")
def test_update_policy_tasks(self, enable_or_disable_win_task):
from .tasks import update_policy_task_fields_task
@patch("autotasks.models.AutomatedTask.create_task_on_agent")
@patch("autotasks.models.AutomatedTask.modify_task_on_agent")
def test_update_policy_tasks(self, modify_task_on_agent, create_task):
from .tasks import update_policy_autotasks_fields_task
# setup data
policy = baker.make("automation.Policy", active=True)
tasks = baker.make(
"autotasks.AutomatedTask", enabled=True, policy=policy, _quantity=3
"autotasks.AutomatedTask",
enabled=True,
policy=policy,
_quantity=3,
)
agent = baker.make_recipe("agents.server_agent", policy=policy)
tasks[0].enabled = False # type: ignore
tasks[0].save() # type: ignore
update_policy_task_fields_task(tasks[0].id) # type: ignore
enable_or_disable_win_task.assert_not_called()
update_policy_autotasks_fields_task(task=tasks[0].id) # type: ignore
modify_task_on_agent.assert_not_called()
self.assertFalse(agent.autotasks.get(parent_task=tasks[0].id).enabled) # type: ignore
update_policy_task_fields_task(tasks[0].id, update_agent=True) # type: ignore
enable_or_disable_win_task.assert_called_with(
agent.autotasks.get(parent_task=tasks[0].id).id, False # type: ignore
)
update_policy_autotasks_fields_task(task=tasks[0].id, update_agent=True) # type: ignore
modify_task_on_agent.assert_not_called()
agent.autotasks.update(sync_status="synced")
update_policy_autotasks_fields_task(task=tasks[0].id, update_agent=True) # type: ignore
modify_task_on_agent.assert_called_once()
@patch("agents.models.Agent.generate_tasks_from_policies")
@patch("agents.models.Agent.generate_checks_from_policies")
@@ -996,17 +951,19 @@ class TestPolicyTasks(TacticalTestCase):
generate_checks.reset_mock()
generate_tasks.reset_mock()
generate_agent_checks_task([agent.pk for agent in agents])
generate_agent_checks_task(agents=[agent.pk for agent in agents])
self.assertEquals(generate_checks.call_count, 5)
generate_tasks.assert_not_called()
generate_checks.reset_mock()
generate_agent_checks_task([agent.pk for agent in agents], create_tasks=True)
generate_agent_checks_task(
agents=[agent.pk for agent in agents], create_tasks=True
)
self.assertEquals(generate_checks.call_count, 5)
self.assertEquals(generate_checks.call_count, 5)
@patch("autotasks.tasks.delete_win_task_schedule.delay")
def test_policy_exclusions(self, delete_task):
@patch("autotasks.models.AutomatedTask.create_task_on_agent")
def test_policy_exclusions(self, create_task):
# setup data
policy = baker.make("automation.Policy", active=True)
baker.make_recipe("checks.memory_check", policy=policy)
@@ -1028,8 +985,6 @@ class TestPolicyTasks(TacticalTestCase):
self.assertEqual(policy.related_agents().count(), 0) # type: ignore
self.assertEqual(agent.agentchecks.count(), 0) # type: ignore
delete_task.assert_called()
delete_task.reset_mock()
# delete agent tasks
agent.autotasks.all().delete()
@@ -1051,8 +1006,6 @@ class TestPolicyTasks(TacticalTestCase):
self.assertEqual(policy.related_agents().count(), 0) # type: ignore
self.assertEqual(agent.agentchecks.count(), 0) # type: ignore
delete_task.assert_called()
delete_task.reset_mock()
# delete agent tasks and reset
agent.autotasks.all().delete()
@@ -1074,8 +1027,6 @@ class TestPolicyTasks(TacticalTestCase):
self.assertEqual(policy.related_agents().count(), 0) # type: ignore
self.assertEqual(agent.agentchecks.count(), 0) # type: ignore
delete_task.assert_called()
delete_task.reset_mock()
# delete agent tasks and reset
agent.autotasks.all().delete()
@@ -1103,11 +1054,82 @@ class TestPolicyTasks(TacticalTestCase):
self.assertEqual(policy.related_agents().count(), 0) # type: ignore
self.assertEqual(agent.agentchecks.count(), 0) # type: ignore
delete_task.assert_called()
delete_task.reset_mock()
def test_removing_duplicate_pending_task_actions(self):
pass
@patch("autotasks.models.AutomatedTask.create_task_on_agent")
def test_policy_inheritance_blocking(self, create_task):
# setup data
policy = baker.make("automation.Policy", active=True)
baker.make_recipe("checks.memory_check", policy=policy)
baker.make("autotasks.AutomatedTask", policy=policy)
agent = baker.make_recipe("agents.agent", monitoring_type="server")
def test_creating_checks_with_assigned_tasks(self):
pass
core = CoreSettings.objects.first()
core.server_policy = policy
core.save()
agent.generate_checks_from_policies()
agent.generate_tasks_from_policies()
# should get policies from default policy
self.assertTrue(agent.autotasks.all())
self.assertTrue(agent.agentchecks.all())
# test client blocking inheritance
agent.site.client.block_policy_inheritance = True
agent.site.client.save()
agent.generate_checks_from_policies()
agent.generate_tasks_from_policies()
self.assertFalse(agent.autotasks.all())
self.assertFalse(agent.agentchecks.all())
agent.site.client.server_policy = policy
agent.site.client.save()
agent.generate_checks_from_policies()
agent.generate_tasks_from_policies()
# should get policies from client policy
self.assertTrue(agent.autotasks.all())
self.assertTrue(agent.agentchecks.all())
# test site blocking inheritance
agent.site.block_policy_inheritance = True
agent.site.save()
agent.generate_checks_from_policies()
agent.generate_tasks_from_policies()
self.assertFalse(agent.autotasks.all())
self.assertFalse(agent.agentchecks.all())
agent.site.server_policy = policy
agent.site.save()
agent.generate_checks_from_policies()
agent.generate_tasks_from_policies()
# should get policies from site policy
self.assertTrue(agent.autotasks.all())
self.assertTrue(agent.agentchecks.all())
# test agent blocking inheritance
agent.block_policy_inheritance = True
agent.save()
agent.generate_checks_from_policies()
agent.generate_tasks_from_policies()
self.assertFalse(agent.autotasks.all())
self.assertFalse(agent.agentchecks.all())
agent.policy = policy
agent.save()
agent.generate_checks_from_policies()
agent.generate_tasks_from_policies()
# should get policies from agent policy
self.assertTrue(agent.autotasks.all())
self.assertTrue(agent.agentchecks.all())

View File

@@ -1,13 +1,12 @@
from django.shortcuts import get_object_or_404
from rest_framework.response import Response
from rest_framework.views import APIView
from agents.models import Agent
from agents.serializers import AgentHostnameSerializer
from autotasks.models import AutomatedTask
from checks.models import Check
from clients.models import Client
from clients.serializers import ClientSerializer, SiteSerializer
from django.shortcuts import get_object_or_404
from rest_framework.response import Response
from rest_framework.views import APIView
from tacticalrmm.utils import notify_error
from winupdate.models import WinUpdatePolicy
from winupdate.serializers import WinUpdatePolicySerializer
@@ -22,7 +21,6 @@ from .serializers import (
PolicyTableSerializer,
PolicyTaskStatusSerializer,
)
from .tasks import run_win_policy_autotask_task
class GetAddPolicies(APIView):
@@ -76,10 +74,10 @@ class GetUpdateDeletePolicy(APIView):
class PolicySync(APIView):
def post(self, request):
if "policy" in request.data.keys():
from automation.tasks import generate_agent_checks_from_policies_task
from automation.tasks import generate_agent_checks_task
generate_agent_checks_from_policies_task.delay(
request.data["policy"], create_tasks=True
generate_agent_checks_task.delay(
policy=request.data["policy"], create_tasks=True
)
return Response("ok")
@@ -101,8 +99,9 @@ class PolicyAutoTask(APIView):
# bulk run win tasks associated with policy
def put(self, request, task):
tasks = AutomatedTask.objects.filter(parent_task=task)
run_win_policy_autotask_task.delay([task.id for task in tasks])
from .tasks import run_win_policy_autotasks_task
run_win_policy_autotasks_task.delay(task=task)
return Response("Affected agent tasks will run shortly")

View File

@@ -0,0 +1,31 @@
# Generated by Django 3.1.7 on 2021-04-04 00:32
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('core', '0019_globalkvstore'),
('scripts', '0007_script_args'),
('autotasks', '0018_automatedtask_run_asap_after_missed'),
]
operations = [
migrations.AddField(
model_name='automatedtask',
name='custom_field',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='autotask', to='core.customfield'),
),
migrations.AddField(
model_name='automatedtask',
name='retvalue',
field=models.TextField(blank=True, null=True),
),
migrations.AlterField(
model_name='automatedtask',
name='script',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='autoscript', to='scripts.script'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.1.7 on 2021-04-21 02:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('autotasks', '0019_auto_20210404_0032'),
]
operations = [
migrations.AlterField(
model_name='automatedtask',
name='sync_status',
field=models.CharField(choices=[('synced', 'Synced With Agent'), ('notsynced', 'Waiting On Agent Checkin'), ('pendingdeletion', 'Pending Deletion on Agent'), ('initial', 'Initial Task Sync')], default='initial', max_length=100),
),
]

View File

@@ -0,0 +1,20 @@
# Generated by Django 3.1.7 on 2021-04-27 14:11
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('core', '0021_customfield_hide_in_ui'),
('autotasks', '0020_auto_20210421_0226'),
]
operations = [
migrations.AlterField(
model_name='automatedtask',
name='custom_field',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='autotasks', to='core.customfield'),
),
]

View File

@@ -1,16 +1,19 @@
import asyncio
import datetime as dt
import random
import string
from typing import List
import pytz
from alerts.models import SEVERITY_CHOICES
from django.conf import settings
from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.db.models.fields import DateTimeField
from loguru import logger
from alerts.models import SEVERITY_CHOICES
from django.utils import timezone as djangotime
from logs.models import BaseAuditModel
from loguru import logger
from packaging import version as pyver
from tacticalrmm.utils import bitdays_to_string
logger.configure(**settings.LOG_CONFIG)
@@ -36,6 +39,7 @@ SYNC_STATUS_CHOICES = [
("synced", "Synced With Agent"),
("notsynced", "Waiting On Agent Checkin"),
("pendingdeletion", "Pending Deletion on Agent"),
("initial", "Initial Task Sync"),
]
TASK_STATUS_CHOICES = [
@@ -60,12 +64,19 @@ class AutomatedTask(BaseAuditModel):
blank=True,
on_delete=models.CASCADE,
)
custom_field = models.ForeignKey(
"core.CustomField",
related_name="autotasks",
null=True,
blank=True,
on_delete=models.SET_NULL,
)
script = models.ForeignKey(
"scripts.Script",
null=True,
blank=True,
related_name="autoscript",
on_delete=models.CASCADE,
on_delete=models.SET_NULL,
)
script_args = ArrayField(
models.CharField(max_length=255, null=True, blank=True),
@@ -100,6 +111,7 @@ class AutomatedTask(BaseAuditModel):
parent_task = models.PositiveIntegerField(null=True, blank=True)
win_task_name = models.CharField(max_length=255, null=True, blank=True)
timeout = models.PositiveIntegerField(default=120)
retvalue = models.TextField(null=True, blank=True)
retcode = models.IntegerField(null=True, blank=True)
stdout = models.TextField(null=True, blank=True)
stderr = models.TextField(null=True, blank=True)
@@ -110,7 +122,7 @@ class AutomatedTask(BaseAuditModel):
max_length=30, choices=TASK_STATUS_CHOICES, default="pending"
)
sync_status = models.CharField(
max_length=100, choices=SYNC_STATUS_CHOICES, default="notsynced"
max_length=100, choices=SYNC_STATUS_CHOICES, default="initial"
)
alert_severity = models.CharField(
max_length=30, choices=SEVERITY_CHOICES, default="info"
@@ -147,6 +159,31 @@ class AutomatedTask(BaseAuditModel):
return self.last_run
# These fields will be duplicated on the agent tasks that are managed by a policy
@property
def policy_fields_to_copy(self) -> List[str]:
return [
"alert_severity",
"email_alert",
"text_alert",
"dashboard_alert",
"script",
"script_args",
"assigned_check",
"name",
"run_time_days",
"run_time_minute",
"run_time_bit_weekdays",
"run_time_date",
"task_type",
"win_task_name",
"timeout",
"enabled",
"remove_if_not_scheduled",
"run_asap_after_missed",
"custom_field",
]
@staticmethod
def generate_task_name():
chars = string.ascii_letters
@@ -160,7 +197,6 @@ class AutomatedTask(BaseAuditModel):
return TaskSerializer(task).data
def create_policy_task(self, agent=None, policy=None):
from .tasks import create_win_task_schedule
# if policy is present, then this task is being copied to another policy
# if agent is present, then this task is being created on an agent from a policy
@@ -177,15 +213,6 @@ class AutomatedTask(BaseAuditModel):
assigned_check = agent.agentchecks.filter(
parent_check=self.assigned_check.pk
).first()
# check was overriden by agent and we need to use that agents check
else:
if agent.agentchecks.filter(
check_type=self.assigned_check.check_type, overriden_by_policy=True
).exists():
assigned_check = agent.agentchecks.filter(
check_type=self.assigned_check.check_type,
overriden_by_policy=True,
).first()
elif policy and self.assigned_check:
if policy.policychecks.filter(name=self.assigned_check.name).exists():
assigned_check = policy.policychecks.filter(
@@ -201,27 +228,175 @@ class AutomatedTask(BaseAuditModel):
policy=policy,
managed_by_policy=bool(agent),
parent_task=(self.pk if agent else None),
alert_severity=self.alert_severity,
email_alert=self.email_alert,
text_alert=self.text_alert,
dashboard_alert=self.dashboard_alert,
script=self.script,
script_args=self.script_args,
assigned_check=assigned_check,
name=self.name,
run_time_days=self.run_time_days,
run_time_minute=self.run_time_minute,
run_time_bit_weekdays=self.run_time_bit_weekdays,
run_time_date=self.run_time_date,
task_type=self.task_type,
win_task_name=self.win_task_name,
timeout=self.timeout,
enabled=self.enabled,
remove_if_not_scheduled=self.remove_if_not_scheduled,
run_asap_after_missed=self.run_asap_after_missed,
)
create_win_task_schedule.delay(task.pk)
for field in self.policy_fields_to_copy:
setattr(task, field, getattr(self, field))
task.save()
task.create_task_on_agent()
def create_task_on_agent(self):
from agents.models import Agent
agent = (
Agent.objects.filter(pk=self.agent.pk)
.only("pk", "version", "hostname", "agent_id")
.first()
)
if self.task_type == "scheduled":
nats_data = {
"func": "schedtask",
"schedtaskpayload": {
"type": "rmm",
"trigger": "weekly",
"weekdays": self.run_time_bit_weekdays,
"pk": self.pk,
"name": self.win_task_name,
"hour": dt.datetime.strptime(self.run_time_minute, "%H:%M").hour,
"min": dt.datetime.strptime(self.run_time_minute, "%H:%M").minute,
},
}
elif self.task_type == "runonce":
# check if scheduled time is in the past
agent_tz = pytz.timezone(agent.timezone)
task_time_utc = self.run_time_date.replace(tzinfo=agent_tz).astimezone(
pytz.utc
)
now = djangotime.now()
if task_time_utc < now:
self.run_time_date = now.astimezone(agent_tz).replace(
tzinfo=pytz.utc
) + djangotime.timedelta(minutes=5)
self.save(update_fields=["run_time_date"])
nats_data = {
"func": "schedtask",
"schedtaskpayload": {
"type": "rmm",
"trigger": "once",
"pk": self.pk,
"name": self.win_task_name,
"year": int(dt.datetime.strftime(self.run_time_date, "%Y")),
"month": dt.datetime.strftime(self.run_time_date, "%B"),
"day": int(dt.datetime.strftime(self.run_time_date, "%d")),
"hour": int(dt.datetime.strftime(self.run_time_date, "%H")),
"min": int(dt.datetime.strftime(self.run_time_date, "%M")),
},
}
if self.run_asap_after_missed and pyver.parse(agent.version) >= pyver.parse(
"1.4.7"
):
nats_data["schedtaskpayload"]["run_asap_after_missed"] = True
if self.remove_if_not_scheduled:
nats_data["schedtaskpayload"]["deleteafter"] = True
elif self.task_type == "checkfailure" or self.task_type == "manual":
nats_data = {
"func": "schedtask",
"schedtaskpayload": {
"type": "rmm",
"trigger": "manual",
"pk": self.pk,
"name": self.win_task_name,
},
}
else:
return "error"
r = asyncio.run(agent.nats_cmd(nats_data, timeout=5))
if r != "ok":
self.sync_status = "initial"
self.save(update_fields=["sync_status"])
logger.warning(
f"Unable to create scheduled task {self.name} on {agent.hostname}. It will be created when the agent checks in."
)
return "timeout"
else:
self.sync_status = "synced"
self.save(update_fields=["sync_status"])
logger.info(f"{agent.hostname} task {self.name} was successfully created")
return "ok"
def modify_task_on_agent(self):
from agents.models import Agent
agent = (
Agent.objects.filter(pk=self.agent.pk)
.only("pk", "version", "hostname", "agent_id")
.first()
)
nats_data = {
"func": "enableschedtask",
"schedtaskpayload": {
"name": self.win_task_name,
"enabled": self.enabled,
},
}
r = asyncio.run(agent.nats_cmd(nats_data, timeout=5))
if r != "ok":
self.sync_status = "notsynced"
self.save(update_fields=["sync_status"])
logger.warning(
f"Unable to modify scheduled task {self.name} on {agent.hostname}. It will try again on next agent checkin"
)
return "timeout"
else:
self.sync_status = "synced"
self.save(update_fields=["sync_status"])
logger.info(f"{agent.hostname} task {self.name} was successfully modified")
return "ok"
def delete_task_on_agent(self):
from agents.models import Agent
agent = (
Agent.objects.filter(pk=self.agent.pk)
.only("pk", "version", "hostname", "agent_id")
.first()
)
nats_data = {
"func": "delschedtask",
"schedtaskpayload": {"name": self.win_task_name},
}
r = asyncio.run(agent.nats_cmd(nats_data, timeout=10))
if r != "ok" and "The system cannot find the file specified" not in r:
self.sync_status = "pendingdeletion"
self.save(update_fields=["sync_status"])
logger.warning(
f"{agent.hostname} task {self.name} was successfully modified"
)
return "timeout"
else:
self.delete()
logger.info(f"{agent.hostname} task {self.name} was deleted")
return "ok"
def run_win_task(self):
from agents.models import Agent
agent = (
Agent.objects.filter(pk=self.agent.pk)
.only("pk", "version", "hostname", "agent_id")
.first()
)
asyncio.run(agent.nats_cmd({"func": "runtask", "taskpk": self.pk}, wait=False))
return "ok"
def should_create_alert(self, alert_template=None):
return (

View File

@@ -4,207 +4,46 @@ import random
from time import sleep
from typing import Union
import pytz
from django.conf import settings
from django.utils import timezone as djangotime
from loguru import logger
from packaging import version as pyver
from logs.models import PendingAction
from tacticalrmm.celery import app
from .models import AutomatedTask
from autotasks.models import AutomatedTask
logger.configure(**settings.LOG_CONFIG)
@app.task
def create_win_task_schedule(pk, pending_action=False):
def create_win_task_schedule(pk):
task = AutomatedTask.objects.get(pk=pk)
if task.task_type == "scheduled":
nats_data = {
"func": "schedtask",
"schedtaskpayload": {
"type": "rmm",
"trigger": "weekly",
"weekdays": task.run_time_bit_weekdays,
"pk": task.pk,
"name": task.win_task_name,
"hour": dt.datetime.strptime(task.run_time_minute, "%H:%M").hour,
"min": dt.datetime.strptime(task.run_time_minute, "%H:%M").minute,
},
}
elif task.task_type == "runonce":
# check if scheduled time is in the past
agent_tz = pytz.timezone(task.agent.timezone)
task_time_utc = task.run_time_date.replace(tzinfo=agent_tz).astimezone(pytz.utc)
now = djangotime.now()
if task_time_utc < now:
task.run_time_date = now.astimezone(agent_tz).replace(
tzinfo=pytz.utc
) + djangotime.timedelta(minutes=5)
task.save(update_fields=["run_time_date"])
nats_data = {
"func": "schedtask",
"schedtaskpayload": {
"type": "rmm",
"trigger": "once",
"pk": task.pk,
"name": task.win_task_name,
"year": int(dt.datetime.strftime(task.run_time_date, "%Y")),
"month": dt.datetime.strftime(task.run_time_date, "%B"),
"day": int(dt.datetime.strftime(task.run_time_date, "%d")),
"hour": int(dt.datetime.strftime(task.run_time_date, "%H")),
"min": int(dt.datetime.strftime(task.run_time_date, "%M")),
},
}
if task.run_asap_after_missed and pyver.parse(
task.agent.version
) >= pyver.parse("1.4.7"):
nats_data["schedtaskpayload"]["run_asap_after_missed"] = True
if task.remove_if_not_scheduled:
nats_data["schedtaskpayload"]["deleteafter"] = True
elif task.task_type == "checkfailure" or task.task_type == "manual":
nats_data = {
"func": "schedtask",
"schedtaskpayload": {
"type": "rmm",
"trigger": "manual",
"pk": task.pk,
"name": task.win_task_name,
},
}
else:
return "error"
r = asyncio.run(task.agent.nats_cmd(nats_data, timeout=10))
if r != "ok":
# don't create pending action if this task was initiated by a pending action
if not pending_action:
# complete any other pending actions on agent with same task_id
task.agent.remove_matching_pending_task_actions(task.id)
PendingAction(
agent=task.agent,
action_type="taskaction",
details={"action": "taskcreate", "task_id": task.id},
).save()
task.sync_status = "notsynced"
task.save(update_fields=["sync_status"])
logger.error(
f"Unable to create scheduled task {task.win_task_name} on {task.agent.hostname}. It will be created when the agent checks in."
)
return
# clear pending action since it was successful
if pending_action:
pendingaction = PendingAction.objects.get(pk=pending_action)
pendingaction.status = "completed"
pendingaction.save(update_fields=["status"])
task.sync_status = "synced"
task.save(update_fields=["sync_status"])
logger.info(f"{task.agent.hostname} task {task.name} was successfully created")
task.create_task_on_agent()
return "ok"
@app.task
def enable_or_disable_win_task(pk, action, pending_action=False):
def enable_or_disable_win_task(pk):
task = AutomatedTask.objects.get(pk=pk)
nats_data = {
"func": "enableschedtask",
"schedtaskpayload": {
"name": task.win_task_name,
"enabled": action,
},
}
r = asyncio.run(task.agent.nats_cmd(nats_data))
if r != "ok":
# don't create pending action if this task was initiated by a pending action
if not pending_action:
PendingAction(
agent=task.agent,
action_type="taskaction",
details={
"action": "tasktoggle",
"value": action,
"task_id": task.id,
},
).save()
task.sync_status = "notsynced"
task.save(update_fields=["sync_status"])
return
# clear pending action since it was successful
if pending_action:
pendingaction = PendingAction.objects.get(pk=pending_action)
pendingaction.status = "completed"
pendingaction.save(update_fields=["status"])
task.sync_status = "synced"
task.save(update_fields=["sync_status"])
task.modify_task_on_agent()
return "ok"
@app.task
def delete_win_task_schedule(pk, pending_action=False):
def delete_win_task_schedule(pk):
task = AutomatedTask.objects.get(pk=pk)
nats_data = {
"func": "delschedtask",
"schedtaskpayload": {"name": task.win_task_name},
}
r = asyncio.run(task.agent.nats_cmd(nats_data, timeout=10))
if r != "ok" and "The system cannot find the file specified" not in r:
# don't create pending action if this task was initiated by a pending action
if not pending_action:
# complete any other pending actions on agent with same task_id
task.agent.remove_matching_pending_task_actions(task.id)
PendingAction(
agent=task.agent,
action_type="taskaction",
details={"action": "taskdelete", "task_id": task.id},
).save()
task.sync_status = "pendingdeletion"
task.save(update_fields=["sync_status"])
return "timeout"
# complete pending action since it was successful
if pending_action:
pendingaction = PendingAction.objects.get(pk=pending_action)
pendingaction.status = "completed"
pendingaction.save(update_fields=["status"])
# complete any other pending actions on agent with same task_id
task.agent.remove_matching_pending_task_actions(task.id)
task.delete()
task.delete_task_on_agent()
return "ok"
@app.task
def run_win_task(pk):
task = AutomatedTask.objects.get(pk=pk)
asyncio.run(task.agent.nats_cmd({"func": "runtask", "taskpk": task.pk}, wait=False))
task.run_win_task()
return "ok"

View File

@@ -4,7 +4,6 @@ from unittest.mock import call, patch
from django.utils import timezone as djangotime
from model_bakery import baker
from logs.models import PendingAction
from tacticalrmm.test import TacticalTestCase
from .models import AutomatedTask
@@ -17,10 +16,10 @@ class TestAutotaskViews(TacticalTestCase):
self.authenticate()
self.setup_coresettings()
@patch("automation.tasks.generate_agent_tasks_from_policies_task.delay")
@patch("automation.tasks.generate_agent_autotasks_task.delay")
@patch("autotasks.tasks.create_win_task_schedule.delay")
def test_add_autotask(
self, create_win_task_schedule, generate_agent_tasks_from_policies_task
self, create_win_task_schedule, generate_agent_autotasks_task
):
url = "/tasks/automatedtasks/"
@@ -84,13 +83,13 @@ class TestAutotaskViews(TacticalTestCase):
"task_type": "manual",
"assigned_check": None,
},
"policy": policy.id,
"policy": policy.id, # type: ignore
}
resp = self.client.post(url, data, format="json")
self.assertEqual(resp.status_code, 200)
generate_agent_tasks_from_policies_task.assert_called_with(policy.id)
generate_agent_autotasks_task.assert_called_with(policy=policy.id) # type: ignore
self.check_not_authenticated("post", url)
@@ -106,14 +105,14 @@ class TestAutotaskViews(TacticalTestCase):
serializer = AutoTaskSerializer(agent)
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp.data, serializer.data)
self.assertEqual(resp.data, serializer.data) # type: ignore
self.check_not_authenticated("get", url)
@patch("autotasks.tasks.enable_or_disable_win_task.delay")
@patch("automation.tasks.update_policy_task_fields_task.delay")
@patch("automation.tasks.update_policy_autotasks_fields_task.delay")
def test_update_autotask(
self, update_policy_task_fields_task, enable_or_disable_win_task
self, update_policy_autotasks_fields_task, enable_or_disable_win_task
):
# setup data
agent = baker.make_recipe("agents.agent")
@@ -125,32 +124,32 @@ class TestAutotaskViews(TacticalTestCase):
resp = self.client.patch("/tasks/500/automatedtasks/", format="json")
self.assertEqual(resp.status_code, 404)
url = f"/tasks/{agent_task.id}/automatedtasks/"
url = f"/tasks/{agent_task.id}/automatedtasks/" # type: ignore
# test editing agent task
data = {"enableordisable": False}
resp = self.client.patch(url, data, format="json")
self.assertEqual(resp.status_code, 200)
enable_or_disable_win_task.assert_called_with(pk=agent_task.id, action=False)
enable_or_disable_win_task.assert_called_with(pk=agent_task.id) # type: ignore
url = f"/tasks/{policy_task.id}/automatedtasks/"
url = f"/tasks/{policy_task.id}/automatedtasks/" # type: ignore
# test editing policy task
data = {"enableordisable": True}
resp = self.client.patch(url, data, format="json")
self.assertEqual(resp.status_code, 200)
update_policy_task_fields_task.assert_called_with(
policy_task.id, update_agent=True
update_policy_autotasks_fields_task.assert_called_with(
task=policy_task.id, update_agent=True # type: ignore
)
self.check_not_authenticated("patch", url)
@patch("autotasks.tasks.delete_win_task_schedule.delay")
@patch("automation.tasks.delete_policy_autotask_task.delay")
@patch("automation.tasks.delete_policy_autotasks_task.delay")
def test_delete_autotask(
self, delete_policy_autotask_task, delete_win_task_schedule
self, delete_policy_autotasks_task, delete_win_task_schedule
):
# setup data
agent = baker.make_recipe("agents.agent")
@@ -163,21 +162,21 @@ class TestAutotaskViews(TacticalTestCase):
self.assertEqual(resp.status_code, 404)
# test delete agent task
url = f"/tasks/{agent_task.id}/automatedtasks/"
url = f"/tasks/{agent_task.id}/automatedtasks/" # type: ignore
resp = self.client.delete(url, format="json")
self.assertEqual(resp.status_code, 200)
delete_win_task_schedule.assert_called_with(pk=agent_task.id)
delete_win_task_schedule.assert_called_with(pk=agent_task.id) # type: ignore
# test delete policy task
url = f"/tasks/{policy_task.id}/automatedtasks/"
url = f"/tasks/{policy_task.id}/automatedtasks/" # type: ignore
resp = self.client.delete(url, format="json")
self.assertEqual(resp.status_code, 200)
delete_policy_autotask_task.assert_called_with(policy_task.id)
delete_policy_autotasks_task.assert_called_with(task=policy_task.id) # type: ignore
self.check_not_authenticated("delete", url)
@patch("agents.models.Agent.nats_cmd")
def test_run_autotask(self, nats_cmd):
@patch("autotasks.tasks.run_win_task.delay")
def test_run_autotask(self, run_win_task):
# setup data
agent = baker.make_recipe("agents.agent", version="1.1.0")
task = baker.make("autotasks.AutomatedTask", agent=agent)
@@ -187,11 +186,10 @@ class TestAutotaskViews(TacticalTestCase):
self.assertEqual(resp.status_code, 404)
# test run agent task
url = f"/tasks/runwintask/{task.id}/"
url = f"/tasks/runwintask/{task.id}/" # type: ignore
resp = self.client.get(url, format="json")
self.assertEqual(resp.status_code, 200)
nats_cmd.assert_called_with({"func": "runtask", "taskpk": task.id}, wait=False)
nats_cmd.reset_mock()
run_win_task.assert_called()
self.check_not_authenticated("get", url)
@@ -284,9 +282,9 @@ class TestAutoTaskCeleryTasks(TacticalTestCase):
run_time_bit_weekdays=127,
run_time_minute="21:55",
)
self.assertEqual(self.task1.sync_status, "notsynced")
self.assertEqual(self.task1.sync_status, "initial")
nats_cmd.return_value = "ok"
ret = create_win_task_schedule.s(pk=self.task1.pk, pending_action=False).apply()
ret = create_win_task_schedule.s(pk=self.task1.pk).apply()
self.assertEqual(nats_cmd.call_count, 1)
nats_cmd.assert_called_with(
{
@@ -301,29 +299,16 @@ class TestAutoTaskCeleryTasks(TacticalTestCase):
"min": 55,
},
},
timeout=10,
timeout=5,
)
self.task1 = AutomatedTask.objects.get(pk=self.task1.pk)
self.assertEqual(self.task1.sync_status, "synced")
nats_cmd.return_value = "timeout"
ret = create_win_task_schedule.s(pk=self.task1.pk, pending_action=False).apply()
ret = create_win_task_schedule.s(pk=self.task1.pk).apply()
self.assertEqual(ret.status, "SUCCESS")
self.task1 = AutomatedTask.objects.get(pk=self.task1.pk)
self.assertEqual(self.task1.sync_status, "notsynced")
# test pending action
self.pending_action = PendingAction.objects.create(
agent=self.agent, action_type="taskaction"
)
self.assertEqual(self.pending_action.status, "pending")
nats_cmd.return_value = "ok"
ret = create_win_task_schedule.s(
pk=self.task1.pk, pending_action=self.pending_action.pk
).apply()
self.assertEqual(ret.status, "SUCCESS")
self.pending_action = PendingAction.objects.get(pk=self.pending_action.pk)
self.assertEqual(self.pending_action.status, "completed")
self.assertEqual(self.task1.sync_status, "initial")
# test runonce with future date
nats_cmd.reset_mock()
@@ -337,7 +322,7 @@ class TestAutoTaskCeleryTasks(TacticalTestCase):
run_time_date=run_time_date,
)
nats_cmd.return_value = "ok"
ret = create_win_task_schedule.s(pk=self.task2.pk, pending_action=False).apply()
ret = create_win_task_schedule.s(pk=self.task2.pk).apply()
nats_cmd.assert_called_with(
{
"func": "schedtask",
@@ -353,7 +338,7 @@ class TestAutoTaskCeleryTasks(TacticalTestCase):
"min": int(dt.datetime.strftime(self.task2.run_time_date, "%M")),
},
},
timeout=10,
timeout=5,
)
self.assertEqual(ret.status, "SUCCESS")
@@ -369,7 +354,7 @@ class TestAutoTaskCeleryTasks(TacticalTestCase):
run_time_date=run_time_date,
)
nats_cmd.return_value = "ok"
ret = create_win_task_schedule.s(pk=self.task3.pk, pending_action=False).apply()
ret = create_win_task_schedule.s(pk=self.task3.pk).apply()
self.task3 = AutomatedTask.objects.get(pk=self.task3.pk)
self.assertEqual(ret.status, "SUCCESS")
@@ -385,7 +370,7 @@ class TestAutoTaskCeleryTasks(TacticalTestCase):
assigned_check=self.check,
)
nats_cmd.return_value = "ok"
ret = create_win_task_schedule.s(pk=self.task4.pk, pending_action=False).apply()
ret = create_win_task_schedule.s(pk=self.task4.pk).apply()
nats_cmd.assert_called_with(
{
"func": "schedtask",
@@ -396,7 +381,7 @@ class TestAutoTaskCeleryTasks(TacticalTestCase):
"name": task_name,
},
},
timeout=10,
timeout=5,
)
self.assertEqual(ret.status, "SUCCESS")
@@ -410,7 +395,7 @@ class TestAutoTaskCeleryTasks(TacticalTestCase):
task_type="manual",
)
nats_cmd.return_value = "ok"
ret = create_win_task_schedule.s(pk=self.task5.pk, pending_action=False).apply()
ret = create_win_task_schedule.s(pk=self.task5.pk).apply()
nats_cmd.assert_called_with(
{
"func": "schedtask",
@@ -421,6 +406,6 @@ class TestAutoTaskCeleryTasks(TacticalTestCase):
"name": task_name,
},
},
timeout=10,
timeout=5,
)
self.assertEqual(ret.status, "SUCCESS")

View File

@@ -1,28 +1,22 @@
import asyncio
from agents.models import Agent
from checks.models import Check
from django.shortcuts import get_object_or_404
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.views import APIView
from agents.models import Agent
from checks.models import Check
from scripts.models import Script
from tacticalrmm.utils import get_bit_days, get_default_timezone, notify_error
from .models import AutomatedTask
from .serializers import AutoTaskSerializer, TaskSerializer
from .tasks import (
create_win_task_schedule,
delete_win_task_schedule,
enable_or_disable_win_task,
)
class AddAutoTask(APIView):
def post(self, request):
from automation.models import Policy
from automation.tasks import generate_agent_tasks_from_policies_task
from automation.tasks import generate_agent_autotasks_task
from autotasks.tasks import create_win_task_schedule
data = request.data
script = get_object_or_404(Script, pk=data["autotask"]["script"])
@@ -47,7 +41,7 @@ class AddAutoTask(APIView):
del data["autotask"]["run_time_days"]
serializer = TaskSerializer(data=data["autotask"], partial=True, context=parent)
serializer.is_valid(raise_exception=True)
obj = serializer.save(
task = serializer.save(
**parent,
script=script,
win_task_name=AutomatedTask.generate_task_name(),
@@ -55,11 +49,11 @@ class AddAutoTask(APIView):
run_time_bit_weekdays=bit_weekdays,
)
if not "policy" in data:
create_win_task_schedule.delay(pk=obj.pk)
if task.agent:
create_win_task_schedule.delay(pk=task.pk)
if "policy" in data:
generate_agent_tasks_from_policies_task.delay(data["policy"])
elif task.policy:
generate_agent_autotasks_task.delay(policy=task.policy.pk)
return Response("Task will be created shortly!")
@@ -75,7 +69,7 @@ class AutoTask(APIView):
return Response(AutoTaskSerializer(agent, context=ctx).data)
def put(self, request, pk):
from automation.tasks import update_policy_task_fields_task
from automation.tasks import update_policy_autotasks_fields_task
task = get_object_or_404(AutomatedTask, pk=pk)
@@ -84,39 +78,44 @@ class AutoTask(APIView):
serializer.save()
if task.policy:
update_policy_task_fields_task.delay(task.pk)
update_policy_autotasks_fields_task.delay(task=task.pk)
return Response("ok")
def patch(self, request, pk):
from automation.tasks import update_policy_task_fields_task
from automation.tasks import update_policy_autotasks_fields_task
from autotasks.tasks import enable_or_disable_win_task
task = get_object_or_404(AutomatedTask, pk=pk)
if "enableordisable" in request.data:
action = request.data["enableordisable"]
if not task.policy:
enable_or_disable_win_task.delay(pk=task.pk, action=action)
else:
update_policy_task_fields_task.delay(task.pk, update_agent=True)
task.enabled = action
task.save(update_fields=["enabled"])
action = "enabled" if action else "disabled"
if task.policy:
update_policy_autotasks_fields_task.delay(
task=task.pk, update_agent=True
)
elif task.agent:
enable_or_disable_win_task.delay(pk=task.pk)
return Response(f"Task will be {action} shortly")
else:
return notify_error("The request was invalid")
def delete(self, request, pk):
from automation.tasks import delete_policy_autotask_task
from automation.tasks import delete_policy_autotasks_task
from autotasks.tasks import delete_win_task_schedule
task = get_object_or_404(AutomatedTask, pk=pk)
if not task.policy:
if task.agent:
delete_win_task_schedule.delay(pk=task.pk)
if task.policy:
delete_policy_autotask_task.delay(task.pk)
elif task.policy:
delete_policy_autotasks_task.delay(task=task.pk)
task.delete()
return Response(f"{task.name} will be deleted shortly")
@@ -124,6 +123,8 @@ class AutoTask(APIView):
@api_view()
def run_task(request, pk):
from autotasks.tasks import run_win_task
task = get_object_or_404(AutomatedTask, pk=pk)
asyncio.run(task.agent.nats_cmd({"func": "runtask", "taskpk": task.pk}, wait=False))
run_win_task.delay(pk=pk)
return Response(f"{task.name} will now be run on {task.agent.hostname}")

View File

@@ -4,17 +4,17 @@ import os
import string
from statistics import mean
from typing import Any
from packaging import version as pyver
import pytz
from alerts.models import SEVERITY_CHOICES
from core.models import CoreSettings
from django.conf import settings
from django.contrib.postgres.fields import ArrayField
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from loguru import logger
from alerts.models import SEVERITY_CHOICES
from core.models import CoreSettings
from logs.models import BaseAuditModel
from loguru import logger
from .utils import bytes2human
@@ -263,6 +263,42 @@ class Check(BaseAuditModel):
"modified_time",
]
@property
def policy_fields_to_copy(self) -> list[str]:
return [
"warning_threshold",
"error_threshold",
"alert_severity",
"name",
"run_interval",
"disk",
"fails_b4_alert",
"ip",
"script",
"script_args",
"info_return_codes",
"warning_return_codes",
"timeout",
"svc_name",
"svc_display_name",
"svc_policy_mode",
"pass_if_start_pending",
"pass_if_svc_not_exist",
"restart_if_stopped",
"log_name",
"event_id",
"event_id_is_wildcard",
"event_type",
"event_source",
"event_message",
"fail_when",
"search_last_days",
"number_of_events_b4_alert",
"email_alert",
"text_alert",
"dashboard_alert",
]
def should_create_alert(self, alert_template=None):
return (
@@ -386,16 +422,20 @@ class Check(BaseAuditModel):
# ping checks
elif self.check_type == "ping":
success = ["Reply", "bytes", "time", "TTL"]
output = data["output"]
if data["has_stdout"]:
if all(x in output for x in success):
self.status = "passing"
else:
if pyver.parse(self.agent.version) <= pyver.parse("1.5.2"):
# DEPRECATED
success = ["Reply", "bytes", "time", "TTL"]
if data["has_stdout"]:
if all(x in output for x in success):
self.status = "passing"
else:
self.status = "failing"
elif data["has_stderr"]:
self.status = "failing"
elif data["has_stderr"]:
self.status = "failing"
else:
self.status = data["status"]
self.more_info = output
self.save(update_fields=["more_info"])
@@ -551,49 +591,23 @@ class Check(BaseAuditModel):
def create_policy_check(self, agent=None, policy=None):
if not agent and not policy or agent and policy:
if (not agent and not policy) or (agent and policy):
return
Check.objects.create(
check = Check.objects.create(
agent=agent,
policy=policy,
managed_by_policy=bool(agent),
parent_check=(self.pk if agent else None),
name=self.name,
alert_severity=self.alert_severity,
check_type=self.check_type,
email_alert=self.email_alert,
dashboard_alert=self.dashboard_alert,
text_alert=self.text_alert,
fails_b4_alert=self.fails_b4_alert,
extra_details=self.extra_details,
run_interval=self.run_interval,
error_threshold=self.error_threshold,
warning_threshold=self.warning_threshold,
disk=self.disk,
ip=self.ip,
script=self.script,
script_args=self.script_args,
timeout=self.timeout,
info_return_codes=self.info_return_codes,
warning_return_codes=self.warning_return_codes,
svc_name=self.svc_name,
svc_display_name=self.svc_display_name,
pass_if_start_pending=self.pass_if_start_pending,
pass_if_svc_not_exist=self.pass_if_svc_not_exist,
restart_if_stopped=self.restart_if_stopped,
svc_policy_mode=self.svc_policy_mode,
log_name=self.log_name,
event_id=self.event_id,
event_id_is_wildcard=self.event_id_is_wildcard,
event_type=self.event_type,
event_source=self.event_source,
event_message=self.event_message,
fail_when=self.fail_when,
search_last_days=self.search_last_days,
number_of_events_b4_alert=self.number_of_events_b4_alert,
)
for field in self.policy_fields_to_copy:
setattr(check, field, getattr(self, field))
check.save()
def is_duplicate(self, check):
if self.check_type == "diskspace":
return self.disk == check.disk

View File

@@ -14,6 +14,22 @@ class TestCheckViews(TacticalTestCase):
self.authenticate()
self.setup_coresettings()
def test_delete_agent_check(self):
# setup data
agent = baker.make_recipe("agents.agent")
check = baker.make_recipe("checks.diskspace_check", agent=agent)
resp = self.client.delete("/checks/500/check/", format="json")
self.assertEqual(resp.status_code, 404)
url = f"/checks/{check.pk}/check/"
resp = self.client.delete(url, format="json")
self.assertEqual(resp.status_code, 200)
self.assertFalse(agent.agentchecks.all())
self.check_not_authenticated("delete", url)
def test_get_disk_check(self):
# setup data
disk_check = baker.make_recipe("checks.diskspace_check")

View File

@@ -1,6 +1,8 @@
import asyncio
from datetime import datetime as dt
from agents.models import Agent
from automation.models import Policy
from django.db.models import Q
from django.shortcuts import get_object_or_404
from django.utils import timezone as djangotime
@@ -8,14 +10,6 @@ from packaging import version as pyver
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.views import APIView
from agents.models import Agent
from automation.models import Policy
from automation.tasks import (
delete_policy_check_task,
generate_agent_checks_from_policies_task,
update_policy_check_fields_task,
)
from scripts.models import Script
from tacticalrmm.utils import notify_error
@@ -25,6 +19,8 @@ from .serializers import CheckHistorySerializer, CheckSerializer
class AddCheck(APIView):
def post(self, request):
from automation.tasks import generate_agent_checks_task
policy = None
agent = None
@@ -53,28 +49,30 @@ class AddCheck(APIView):
data=request.data["check"], partial=True, context=parent
)
serializer.is_valid(raise_exception=True)
obj = serializer.save(**parent, script=script)
new_check = serializer.save(**parent, script=script)
# Generate policy Checks
if policy:
generate_agent_checks_from_policies_task.delay(policypk=policy.pk)
generate_agent_checks_task.delay(policy=policy.pk)
elif agent:
checks = agent.agentchecks.filter( # type: ignore
check_type=obj.check_type, managed_by_policy=True
check_type=new_check.check_type, managed_by_policy=True
)
# Should only be one
duplicate_check = [check for check in checks if check.is_duplicate(obj)]
duplicate_check = [
check for check in checks if check.is_duplicate(new_check)
]
if duplicate_check:
policy = Check.objects.get(pk=duplicate_check[0].parent_check).policy
if policy.enforced:
obj.overriden_by_policy = True
obj.save()
new_check.overriden_by_policy = True
new_check.save()
else:
duplicate_check[0].delete()
return Response(f"{obj.readable_desc} was added!")
return Response(f"{new_check.readable_desc} was added!")
class GetUpdateDeleteCheck(APIView):
@@ -83,6 +81,10 @@ class GetUpdateDeleteCheck(APIView):
return Response(CheckSerializer(check).data)
def patch(self, request, pk):
from automation.tasks import (
update_policy_check_fields_task,
)
check = get_object_or_404(Check, pk=pk)
# remove fields that should not be changed when editing a check from the frontend
@@ -105,36 +107,31 @@ class GetUpdateDeleteCheck(APIView):
serializer = CheckSerializer(instance=check, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
obj = serializer.save()
# Update policy check fields
if check.policy:
update_policy_check_fields_task(checkpk=pk)
check = serializer.save()
# resolve any alerts that are open
if "check_reset" in request.data.keys():
if obj.alert.filter(resolved=False).exists():
obj.alert.get(resolved=False).resolve()
if check.alert.filter(resolved=False).exists():
check.alert.get(resolved=False).resolve()
return Response(f"{obj.readable_desc} was edited!")
if check.policy:
update_policy_check_fields_task.delay(check=check.pk)
return Response(f"{check.readable_desc} was edited!")
def delete(self, request, pk):
from automation.tasks import generate_agent_checks_task
check = get_object_or_404(Check, pk=pk)
check_pk = check.pk
policy_pk = None
if check.policy:
policy_pk = check.policy.pk
check.delete()
# Policy check deleted
if check.policy:
delete_policy_check_task.delay(checkpk=check_pk)
Check.objects.filter(parent_check=check.pk).delete()
# Re-evaluate agent checks is policy was enforced
if check.policy.enforced:
generate_agent_checks_from_policies_task.delay(policypk=policy_pk)
generate_agent_checks_task.delay(policy=check.policy)
# Agent check deleted
elif check.agent:

View File

@@ -0,0 +1,23 @@
# Generated by Django 3.1.7 on 2021-04-17 01:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('clients', '0016_auto_20210329_1827'),
]
operations = [
migrations.AddField(
model_name='client',
name='block_policy_inheritance',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='site',
name='block_policy_inheritance',
field=models.BooleanField(default=False),
),
]

View File

@@ -9,6 +9,7 @@ from logs.models import BaseAuditModel
class Client(BaseAuditModel):
name = models.CharField(max_length=255, unique=True)
block_policy_inheritance = models.BooleanField(default=False)
workstation_policy = models.ForeignKey(
"automation.Policy",
related_name="workstation_clients",
@@ -34,30 +35,29 @@ class Client(BaseAuditModel):
def save(self, *args, **kw):
from alerts.tasks import cache_agents_alert_template
from automation.tasks import generate_agent_checks_by_location_task
from automation.tasks import generate_agent_checks_task
# get old client if exists
old_client = type(self).objects.get(pk=self.pk) if self.pk else None
super(BaseAuditModel, self).save(*args, **kw)
# check if server polcies have changed and initiate task to reapply policies if so
if old_client and old_client.server_policy != self.server_policy:
generate_agent_checks_by_location_task.delay(
location={"site__client_id": self.pk},
mon_type="server",
create_tasks=True,
)
# check if polcies have changed and initiate task to reapply policies if so
if old_client:
if (
(old_client.server_policy != self.server_policy)
or (old_client.workstation_policy != self.workstation_policy)
or (
old_client.block_policy_inheritance != self.block_policy_inheritance
)
):
# check if workstation polcies have changed and initiate task to reapply policies if so
if old_client and old_client.workstation_policy != self.workstation_policy:
generate_agent_checks_by_location_task.delay(
location={"site__client_id": self.pk},
mon_type="workstation",
create_tasks=True,
)
generate_agent_checks_task.delay(
client=self.pk,
create_tasks=True,
)
if old_client and old_client.alert_template != self.alert_template:
cache_agents_alert_template.delay()
if old_client.alert_template != self.alert_template:
cache_agents_alert_template.delay()
class Meta:
ordering = ("name",)
@@ -120,6 +120,7 @@ class Client(BaseAuditModel):
class Site(BaseAuditModel):
client = models.ForeignKey(Client, related_name="sites", on_delete=models.CASCADE)
name = models.CharField(max_length=255)
block_policy_inheritance = models.BooleanField(default=False)
workstation_policy = models.ForeignKey(
"automation.Policy",
related_name="workstation_sites",
@@ -145,30 +146,24 @@ class Site(BaseAuditModel):
def save(self, *args, **kw):
from alerts.tasks import cache_agents_alert_template
from automation.tasks import generate_agent_checks_by_location_task
from automation.tasks import generate_agent_checks_task
# get old client if exists
old_site = type(self).objects.get(pk=self.pk) if self.pk else None
super(Site, self).save(*args, **kw)
# check if server polcies have changed and initiate task to reapply policies if so
if old_site and old_site.server_policy != self.server_policy:
generate_agent_checks_by_location_task.delay(
location={"site_id": self.pk},
mon_type="server",
create_tasks=True,
)
# check if polcies have changed and initiate task to reapply policies if so
if old_site:
if (
(old_site.server_policy != self.server_policy)
or (old_site.workstation_policy != self.workstation_policy)
or (old_site.block_policy_inheritance != self.block_policy_inheritance)
):
# check if workstation polcies have changed and initiate task to reapply policies if so
if old_site and old_site.workstation_policy != self.workstation_policy:
generate_agent_checks_by_location_task.delay(
location={"site_id": self.pk},
mon_type="workstation",
create_tasks=True,
)
generate_agent_checks_task.delay(site=self.pk, create_tasks=True)
if old_site and old_site.alert_template != self.alert_template:
cache_agents_alert_template.delay()
if old_site.alert_template != self.alert_template:
cache_agents_alert_template.delay()
class Meta:
ordering = ("name",)

View File

@@ -39,6 +39,7 @@ class SiteSerializer(ModelSerializer):
"client",
"custom_fields",
"agent_count",
"block_policy_inheritance",
)
def validate(self, val):
@@ -80,6 +81,7 @@ class ClientSerializer(ModelSerializer):
"server_policy",
"workstation_policy",
"alert_template",
"block_policy_inheritance",
"sites",
"custom_fields",
"agent_count",

View File

@@ -179,13 +179,9 @@ class TestClientViews(TacticalTestCase):
self.check_not_authenticated("put", url)
@patch("automation.tasks.generate_all_agent_checks_task.delay")
@patch("automation.tasks.generate_all_agent_checks_task.delay")
def test_delete_client(self, task1, task2):
def test_delete_client(self):
from agents.models import Agent
task1.return_value = "ok"
task2.return_value = "ok"
# setup data
client_to_delete = baker.make("clients.Client")
client_to_move = baker.make("clients.Client")
@@ -352,13 +348,9 @@ class TestClientViews(TacticalTestCase):
self.check_not_authenticated("put", url)
@patch("automation.tasks.generate_all_agent_checks_task.delay")
@patch("automation.tasks.generate_all_agent_checks_task.delay")
def test_delete_site(self, task1, task2):
def test_delete_site(self):
from agents.models import Agent
task1.return_value = "ok"
task2.return_value = "ok"
# setup data
client = baker.make("clients.Client")
site_to_delete = baker.make("clients.Site", client=client)

View File

@@ -111,7 +111,7 @@ class GetUpdateClient(APIView):
class DeleteClient(APIView):
def delete(self, request, pk, sitepk):
from automation.tasks import generate_all_agent_checks_task
from automation.tasks import generate_agent_checks_task
client = get_object_or_404(Client, pk=pk)
agents = Agent.objects.filter(site__client=client)
@@ -124,8 +124,7 @@ class DeleteClient(APIView):
site = get_object_or_404(Site, pk=sitepk)
agents.update(site=site)
generate_all_agent_checks_task.delay("workstation", create_tasks=True)
generate_all_agent_checks_task.delay("server", create_tasks=True)
generate_agent_checks_task.delay(all=True, create_tasks=True)
client.delete()
return Response(f"{client.name} was deleted!")
@@ -207,7 +206,7 @@ class GetUpdateSite(APIView):
class DeleteSite(APIView):
def delete(self, request, pk, sitepk):
from automation.tasks import generate_all_agent_checks_task
from automation.tasks import generate_agent_checks_task
site = get_object_or_404(Site, pk=pk)
if site.client.sites.count() == 1:
@@ -224,8 +223,7 @@ class DeleteSite(APIView):
agents.update(site=agent_site)
generate_all_agent_checks_task.delay("workstation", create_tasks=True)
generate_all_agent_checks_task.delay("server", create_tasks=True)
generate_agent_checks_task.delay(all=True, create_tasks=True)
site.delete()
return Response(f"{site.name} was deleted!")

View File

@@ -1,12 +1,8 @@
import os
import shutil
import subprocess
import tempfile
from django.core.management.base import BaseCommand
from agents.models import Agent
from scripts.models import Script
from logs.models import PendingAction
class Command(BaseCommand):
@@ -29,5 +25,8 @@ class Command(BaseCommand):
self.style.SUCCESS(f"Migrated disks on {agent.hostname}")
)
# remove task pending actions. deprecated 4/20/2021
PendingAction.objects.filter(action_type="taskaction").delete()
# load community scripts into the db
Script.load_community_scripts()

View File

@@ -0,0 +1,21 @@
# Generated by Django 3.1.7 on 2021-04-04 00:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0018_auto_20210329_1709'),
]
operations = [
migrations.CreateModel(
name='GlobalKVStore',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=25)),
('value', models.TextField()),
],
),
]

View File

@@ -0,0 +1,14 @@
# Generated by Django 3.1.7 on 2021-04-15 01:32
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0019_codesigntoken'),
('core', '0019_globalkvstore'),
]
operations = [
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.1.7 on 2021-04-24 23:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0020_merge_20210415_0132'),
]
operations = [
migrations.AddField(
model_name='customfield',
name='hide_in_ui',
field=models.BooleanField(default=False),
),
]

View File

@@ -79,7 +79,7 @@ class CoreSettings(BaseAuditModel):
def save(self, *args, **kwargs):
from alerts.tasks import cache_agents_alert_template
from automation.tasks import generate_all_agent_checks_task
from automation.tasks import generate_agent_checks_task
if not self.pk and CoreSettings.objects.exists():
raise ValidationError("There can only be one CoreSettings instance")
@@ -97,14 +97,10 @@ class CoreSettings(BaseAuditModel):
super(BaseAuditModel, self).save(*args, **kwargs)
# check if server polcies have changed and initiate task to reapply policies if so
if old_settings and old_settings.server_policy != self.server_policy:
generate_all_agent_checks_task.delay(mon_type="server", create_tasks=True)
# check if workstation polcies have changed and initiate task to reapply policies if so
if old_settings and old_settings.workstation_policy != self.workstation_policy:
generate_all_agent_checks_task.delay(
mon_type="workstation", create_tasks=True
)
if (old_settings and old_settings.server_policy != self.server_policy) or (
old_settings and old_settings.workstation_policy != self.workstation_policy
):
generate_agent_checks_task.delay(all=True, create_tasks=True)
if old_settings and old_settings.alert_template != self.alert_template:
cache_agents_alert_template.delay()
@@ -251,6 +247,7 @@ class CustomField(models.Model):
blank=True,
default=list,
)
hide_in_ui = models.BooleanField(default=False)
class Meta:
unique_together = (("model", "name"),)
@@ -279,3 +276,56 @@ class CodeSignToken(models.Model):
def __str__(self):
return "Code signing token"
class GlobalKVStore(models.Model):
name = models.CharField(max_length=25)
value = models.TextField()
def __str__(self):
return self.name
RUN_ON_CHOICES = (
("client", "Client"),
("site", "Site"),
("agent", "Agent"),
("once", "Once"),
)
SCHEDULE_CHOICES = (("daily", "Daily"), ("weekly", "Weekly"), ("monthly", "Monthly"))
""" class GlobalTask(models.Model):
script = models.ForeignKey(
"scripts.Script",
null=True,
blank=True,
related_name="script",
on_delete=models.SET_NULL,
)
script_args = ArrayField(
models.CharField(max_length=255, null=True, blank=True),
null=True,
blank=True,
default=list,
)
custom_field = models.OneToOneField(
"core.CustomField",
related_name="globaltask",
null=True,
blank=True,
on_delete=models.SET_NULL,
)
timeout = models.PositiveIntegerField(default=120)
retcode = models.IntegerField(null=True, blank=True)
retvalue = models.TextField(null=True, blank=True)
stdout = models.TextField(null=True, blank=True)
stderr = models.TextField(null=True, blank=True)
execution_time = models.CharField(max_length=100, default="0.0000")
run_schedule = models.CharField(
max_length=25, choices=SCHEDULE_CHOICES, default="once"
)
run_on = models.CharField(
max_length=25, choices=RUN_ON_CHOICES, default="once"
) """

View File

@@ -1,7 +1,7 @@
import pytz
from rest_framework import serializers
from .models import CodeSignToken, CoreSettings, CustomField
from .models import CodeSignToken, CoreSettings, CustomField, GlobalKVStore
class CoreSettingsSerializer(serializers.ModelSerializer):
@@ -33,3 +33,9 @@ class CodeSignTokenSerializer(serializers.ModelSerializer):
class Meta:
model = CodeSignToken
fields = "__all__"
class KeyStoreSerializer(serializers.ModelSerializer):
class Meta:
model = GlobalKVStore
fields = "__all__"

View File

@@ -8,8 +8,8 @@ from model_bakery import baker
from tacticalrmm.test import TacticalTestCase
from .consumers import DashInfo
from .models import CoreSettings, CustomField
from .serializers import CustomFieldSerializer
from .models import CoreSettings, CustomField, GlobalKVStore
from .serializers import CustomFieldSerializer, KeyStoreSerializer
from .tasks import core_maintenance_tasks
@@ -88,8 +88,8 @@ class TestCoreTasks(TacticalTestCase):
self.check_not_authenticated("get", url)
@patch("automation.tasks.generate_all_agent_checks_task.delay")
def test_edit_coresettings(self, generate_all_agent_checks_task):
@patch("automation.tasks.generate_agent_checks_task.delay")
def test_edit_coresettings(self, generate_agent_checks_task):
url = "/core/editsettings/"
# setup
@@ -106,7 +106,7 @@ class TestCoreTasks(TacticalTestCase):
)
self.assertEqual(CoreSettings.objects.first().mesh_token, data["mesh_token"])
generate_all_agent_checks_task.assert_not_called()
generate_agent_checks_task.assert_not_called()
# test adding policy
data = {
@@ -120,9 +120,9 @@ class TestCoreTasks(TacticalTestCase):
CoreSettings.objects.first().workstation_policy.id, policies[0].id # type: ignore
)
self.assertEqual(generate_all_agent_checks_task.call_count, 2)
generate_agent_checks_task.assert_called_once()
generate_all_agent_checks_task.reset_mock()
generate_agent_checks_task.reset_mock()
# test remove policy
data = {
@@ -132,7 +132,7 @@ class TestCoreTasks(TacticalTestCase):
self.assertEqual(r.status_code, 200)
self.assertEqual(CoreSettings.objects.first().workstation_policy, None)
self.assertEqual(generate_all_agent_checks_task.call_count, 1)
self.assertEqual(generate_agent_checks_task.call_count, 1)
self.check_not_authenticated("patch", url)
@@ -273,3 +273,61 @@ class TestCoreTasks(TacticalTestCase):
self.assertFalse(CustomField.objects.filter(pk=custom_field.id).exists()) # type: ignore
self.check_not_authenticated("delete", url)
def test_get_keystore(self):
url = "/core/keystore/"
# setup
keys = baker.make("core.GlobalKVStore", _quantity=2)
r = self.client.get(url)
serializer = KeyStoreSerializer(keys, many=True)
self.assertEqual(r.status_code, 200)
self.assertEqual(len(r.data), 2) # type: ignore
self.assertEqual(r.data, serializer.data) # type: ignore
self.check_not_authenticated("get", url)
def test_add_keystore(self):
url = "/core/keystore/"
data = {"name": "test", "value": "text"}
r = self.client.post(url, data)
self.assertEqual(r.status_code, 200)
self.check_not_authenticated("post", url)
def test_update_keystore(self):
# setup
key = baker.make("core.GlobalKVStore")
# test not found
r = self.client.put("/core/keystore/500/")
self.assertEqual(r.status_code, 404)
url = f"/core/keystore/{key.id}/" # type: ignore
data = {"name": "test", "value": "text"}
r = self.client.put(url, data)
self.assertEqual(r.status_code, 200)
new_key = GlobalKVStore.objects.get(pk=key.id) # type: ignore
self.assertEqual(new_key.name, data["name"])
self.assertEqual(new_key.value, data["value"])
self.check_not_authenticated("put", url)
def test_delete_keystore(self):
# setup
key = baker.make("core.GlobalKVStore")
# test not found
r = self.client.delete("/core/keystore/500/")
self.assertEqual(r.status_code, 404)
url = f"/core/keystore/{key.id}/" # type: ignore
r = self.client.delete(url)
self.assertEqual(r.status_code, 200)
self.assertFalse(GlobalKVStore.objects.filter(pk=key.id).exists()) # type: ignore
self.check_not_authenticated("delete", url)

View File

@@ -13,4 +13,6 @@ urlpatterns = [
path("customfields/", views.GetAddCustomFields.as_view()),
path("customfields/<int:pk>/", views.GetUpdateDeleteCustomFields.as_view()),
path("codesign/", views.CodeSign.as_view()),
path("keystore/", views.GetAddKeyStore.as_view()),
path("keystore/<int:pk>/", views.UpdateDeleteKeyStore.as_view()),
]

View File

@@ -11,11 +11,12 @@ from rest_framework.views import APIView
from tacticalrmm.utils import notify_error
from .models import CodeSignToken, CoreSettings, CustomField
from .models import CodeSignToken, CoreSettings, CustomField, GlobalKVStore
from .serializers import (
CodeSignTokenSerializer,
CoreSettingsSerializer,
CustomFieldSerializer,
KeyStoreSerializer,
)
@@ -61,9 +62,12 @@ def version(request):
@api_view()
def dashboard_info(request):
from tacticalrmm.utils import get_latest_trmm_ver
return Response(
{
"trmm_version": settings.TRMM_VERSION,
"latest_trmm_ver": get_latest_trmm_ver(),
"dark_mode": request.user.dark_mode,
"show_community_scripts": request.user.show_community_scripts,
"dbl_click_action": request.user.agent_dblclick_action,
@@ -229,3 +233,32 @@ class CodeSign(APIView):
except:
ret = "Something went wrong"
return notify_error(ret)
class GetAddKeyStore(APIView):
def get(self, request):
keys = GlobalKVStore.objects.all()
return Response(KeyStoreSerializer(keys, many=True).data)
def post(self, request):
serializer = KeyStoreSerializer(data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response("ok")
class UpdateDeleteKeyStore(APIView):
def put(self, request, pk):
key = get_object_or_404(GlobalKVStore, pk=pk)
serializer = KeyStoreSerializer(instance=key, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response("ok")
def delete(self, request, pk):
get_object_or_404(GlobalKVStore, pk=pk).delete()
return Response("ok")

View File

@@ -7,7 +7,7 @@ from tacticalrmm.middleware import get_debug_info, get_username
ACTION_TYPE_CHOICES = [
("schedreboot", "Scheduled Reboot"),
("taskaction", "Scheduled Task Action"),
("taskaction", "Scheduled Task Action"), # deprecated
("agentupdate", "Agent Update"),
("chocoinstall", "Chocolatey Software Install"),
]
@@ -42,13 +42,6 @@ AUDIT_OBJECT_TYPE_CHOICES = [
("bulk", "Bulk"),
]
# taskaction details format
# {
# "action": "taskcreate" | "taskdelete" | "tasktoggle",
# "value": "Enable" | "Disable" # only needed for task toggle,
# "task_id": 1
# }
STATUS_CHOICES = [
("pending", "Pending"),
("completed", "Completed"),
@@ -250,8 +243,6 @@ class PendingAction(models.Model):
if self.action_type == "schedreboot":
obj = dt.datetime.strptime(self.details["time"], "%Y-%m-%d %H:%M:%S")
return dt.datetime.strftime(obj, "%B %d, %Y at %I:%M %p")
elif self.action_type == "taskaction":
return "Next agent check-in"
elif self.action_type == "agentupdate":
return "Next update cycle"
elif self.action_type == "chocoinstall":
@@ -268,20 +259,6 @@ class PendingAction(models.Model):
elif self.action_type == "chocoinstall":
return f"{self.details['name']} software install"
elif self.action_type == "taskaction":
if self.details["action"] == "taskdelete":
return "Device pending task deletion"
elif self.details["action"] == "taskcreate":
return "Device pending task creation"
elif self.details["action"] == "tasktoggle":
# value is bool
if self.details["value"]:
action = "enable"
else:
action = "disable"
return f"Device pending task {action}"
class BaseAuditModel(models.Model):
# abstract base class for auditing models

View File

@@ -262,23 +262,13 @@
},
{
"guid": "d980fda3-a068-47eb-8495-1aab07a24e64",
"filename": "Win_Defender_Status_Report_Last24hrs.ps1",
"filename": "Win_Defender_Status_Report.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Defender - Status Report Last 24hr",
"description": "Using Event Viewer, this will check for Malware and Antispyware within the last 24 hours and display, otherwise will report as Healthy",
"name": "Defender - Status Report",
"description": "This will check for Malware and Antispyware within the last 24 hours and display, otherwise will report as Healthy. Command Parameter: (number) if provided will check that number of days back in the log.",
"shell": "powershell",
"category": "TRMM (Win):Security>Antivirus"
},
{
"guid": "6a0202fc-1b27-4905-95c0-0a03bb881893",
"filename": "Win_Defender_Status_Report_LastYear.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "Defender - Status Report Last Year",
"description": "Using Event Viewer, this will check for Malware and Antispyware and display, otherwise will report as Healthy",
"shell": "powershell",
"category": "TRMM (Win):Security>Antivirus",
"default_timeout": "300"
},
{
"guid": "9956e936-6fdb-4488-a9d8-8b274658037f",
"filename": "Win_Disable_Fast_Startup.bat",
@@ -609,5 +599,25 @@
"description": "Add a task to Task Scheduler, needs editing",
"shell": "powershell",
"category": "TRMM (Win):Other"
},
{
"guid": "17040742-184a-4251-8f7b-4a1b0a1f02d1",
"filename": "Win_File_Copy_Misc.ps1",
"submittedBy": "https://github.com/tremor021",
"name": "EXAMPLE File Copying using powershell",
"description": "Reference Script: Will need manual tweaking, for copying files/folders from paths/websites to local",
"shell": "powershell",
"category": "TRMM (Win):Misc>Reference",
"default_timeout": "1"
},
{
"guid": "168037d8-78e6-4a6a-a9a9-8ec2c1dbe949",
"filename": "Win_MSI_Install.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "EXAMPLE Function for running MSI install via powershell",
"description": "Reference Script: Will need manual tweaking, for running MSI from powershell",
"shell": "powershell",
"category": "TRMM (Win):Misc>Reference",
"default_timeout": "1"
}
]

View File

@@ -196,9 +196,9 @@ class Script(BaseAuditModel):
def parse_script_args(
cls, agent, shell: str, args: List[str] = list()
) -> Union[List[str], None]:
from core.models import CustomField
from core.models import CustomField, GlobalKVStore
if not list:
if not args:
return []
temp_args = list()
@@ -220,6 +220,18 @@ class Script(BaseAuditModel):
# ignore arg since it is invalid
continue
# value is in the global keystore and replace value
if temp[0] == "global":
if GlobalKVStore.objects.filter(name=temp[1]).exists():
value = GlobalKVStore.objects.get(name=temp[1]).value
temp_args.append(
re.sub("\\{\\{.*\\}\\}", "'" + value + "'", arg)
)
continue
else:
# ignore since value doesn't exist
continue
if temp[0] == "client":
model = "client"
obj = agent.client
@@ -263,7 +275,7 @@ class Script(BaseAuditModel):
# replace the value in the arg and push to array
# log any unhashable type errors
try:
temp_args.append(re.sub("\\{\\{.*\\}\\}", value, arg)) # type: ignore
temp_args.append(re.sub("\\{\\{.*\\}\\}", "'" + value + "'", arg)) # type: ignore
except Exception as e:
logger.error(e)
continue

View File

@@ -15,16 +15,16 @@ EXE_DIR = os.path.join(BASE_DIR, "tacticalrmm/private/exe")
AUTH_USER_MODEL = "accounts.User"
# latest release
TRMM_VERSION = "0.6.2"
TRMM_VERSION = "0.6.6"
# bump this version everytime vue code is changed
# to alert user they need to manually refresh their browser
APP_VER = "0.0.131"
APP_VER = "0.0.133"
# https://github.com/wh1te909/rmmagent
LATEST_AGENT_VER = "1.5.2"
LATEST_AGENT_VER = "1.5.3"
MESH_VER = "0.8.17"
MESH_VER = "0.8.19"
# for the update script, bump when need to recreate venv or npm install
PIP_VER = "15"

View File

@@ -66,8 +66,7 @@ class TestUtils(TestCase):
mock_subprocess.assert_not_called()
@override_settings(
ALLOWED_HOSTS=["api.example.com"],
SECRET_KEY="sekret",
ALLOWED_HOSTS=["api.example.com"], SECRET_KEY="sekret", DOCKER_BUILD=False
)
@patch("subprocess.run")
def test_reload_nats(self, mock_subprocess):

View File

@@ -263,3 +263,20 @@ def run_nats_api_cmd(mode: str, ids: list[str], timeout: int = 30) -> None:
subprocess.run(cmd, capture_output=True, timeout=timeout)
except Exception as e:
logger.error(e)
def get_latest_trmm_ver() -> str:
url = "https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/api/tacticalrmm/tacticalrmm/settings.py"
try:
r = requests.get(url, timeout=5)
except:
return "error"
try:
for line in r.text.splitlines():
if "TRMM_VERSION" in line:
return line.split(" ")[2].strip('"')
except Exception as e:
logger.error(e)
return "error"

View File

@@ -15,14 +15,14 @@ Needs an official install_devbox.sh script
## Install Ubuntu 20.04 LTS
Don't forget to
```
```bash
sudo apt-get updates && sudo apt-get upgrade
```
### Optional
Set all users in sudo group not to require password every time:
```
```bash
sudo visudo
```
@@ -36,14 +36,14 @@ Add this:
Create folder to dump into
```
```bash
sudo mkdir /rmm
sudo chown ${USER}:${USER} -R /rmm
cd /rmm
```
Get dev install script
```
```bash
wget https://raw.githubusercontent.com/silversword411/tacticalrmm-devdocs/blob/main/install_devbox.sh
```
@@ -53,7 +53,7 @@ and replace with your forked repo URL (example commented out below)
## Run it
```
```bash
./install_devbox.sh
```
## Watch for
@@ -98,7 +98,16 @@ pip install -r requirements.txt
Then run tests
```
```bash
python manage.py test
```
## Misc Notes
### Spinning up front end web interface in development
```bash
cd /web
npm run serve
```

View File

@@ -0,0 +1,59 @@
## Install WSL2
https://docs.microsoft.com/en-us/windows/wsl/install-win10
## Install Docker Desktop
https://www.docker.com/products/docker-desktop
### Configure Docker
Make sure it doesn't look like this
![img](images/docker_WSL2_distros_missing.png)
This is better
![img](images/docker_with_ubuntu-20.04.png)
### Check and make sure WSL is v2 and set Ubuntu as default
[https://docs.microsoft.com/en-us/windows/wsl/install-win10#set-your-distribution-version-to-wsl-1-or-wsl-2](https://docs.microsoft.com/en-us/windows/wsl/install-win10#set-your-distribution-version-to-wsl-1-or-wsl-2)
![img](images/wls2_upgrade_and_set_default.png)
## Create .env file
Under .devcontainer duplicate
```
.env.example
```
as
```
.env
```
Customize to your tastes (it doesn't need to be internet configured, just add records in your `hosts` file) eg
```
127.0.0.1 rmm.example.com
127.0.0.1 api.example.com
127.0.0.1 mesh.example.com
```
## View mkdocks live edits in browser
Change stuff in `/docs/docs/`
mkdocs is Exposed on Port: 8005
Open: [http://rmm.example.com:8005/](http://rmm.example.com:8005/)
## View django administration
Open: [http://rmm.example.com:8000/admin/](http://rmm.example.com:8000/admin/)

View File

@@ -84,6 +84,14 @@ Bring changes from original repo to your local vscode copy so you're current wit
![Sync Fork](images/trmm_need_sync_local_fork.png)
In VSCode open TERMINAL
```
Ctrl+`
```
Tell git to pull from the GitHub upstream repo all new changes into your local directory
```
git pull --rebase upstream develop
```

View File

@@ -0,0 +1,9 @@
# Automated Tasks
## Collector Tasks
Collector tasks allow saving data from script output directly to a custom field. The collector task will only save the last line of standard output of the script.
You can create collector tasks by adding it to an Automation Policy or adding it directly to an agent. During creation, select the **Collector** checkbox and select the custom field to save to. You can only save to agent custom fields at this time.
See [Custom Fields](custom_fields.md) and [Scripting](scripting.md) for more information

View File

@@ -1,16 +1,53 @@
# Custom Fields
**Settings > Global Settings > Custom Fields**
!!!info
v0.5.0 adds support for custom fields to be used in the dashboard and in scripts.
v0.5.0 adds support for custom fields to be used in scripts.
#### Adding Custom Fields
It also exposes some pre-defined fields that are already in the database.
In the dashboard, go to **Settings > Global Settings > Custom Fields** and click **Add Custom Field**.
Please check the following video for examples until proper docs are written:
The following options are available to configure on custom fields:
[https://www.youtube.com/watch?v=0-5jGGL3FOM](https://www.youtube.com/watch?v=0-5jGGL3FOM)
- **Model** - This is the object that the custom field will be added to. The available options are:
- Agent
- Site
- Client
- **Name** - Sets the name of the custom field. This will be used to identify the custom field in the dashboard and in scripts.
- **Field Type** - Sets the type of field. Below are the allowed types.
- Text
- Number
- Single select dropdown
- Multi-select dropdown
- Checkbox
- DateTime
- **Input Options** - *Only available on Single and Multiple-select dropdowns*. Sets the options to choose from.
- **Default Value** - If no value is found when looking up the custom field; this value will instead be supplied.
- **Required** - This makes the field required when adding new Clients, Sites, and Agents. *If this is set a default value will need to be set as well*
- **Hide in Dashboard** - This will not show the custom field in Client, Site, and Agent forms in the dashboard. This is useful if the custom field's value is updated by a collector task and only supplied to scripts.
#### Using Custom Fields in the Dashboard
Once the custom fields are added, they will show up in the Client, Site, and Agent Add/Edit forms.
#### Using Custom Fields in Scripts
Tactical RMM allows for passing various database fields for Clients, Sites, and Agents in scripts. This includes custom fields as well!
!!!warning
The characters within the brackets is case-sensitive!
In your script's arguments, use the notation `{{client.AV_KEY}}`. This will lookup the client for the agent that the script is running on and find the custom field named `AV_KEY` and replace that with the value.
The same is also true for `{{site.no_patching}}` and `{{agent.Another Field}}`
For more information see SCRIPTING PAGE
#### Populating Custom Fields automatically
Tactical RMM supports automatically collecting information and saving them directly to custom fields. This is made possible by creating **Collector Tasks**. These are just normal Automated Tasks, but instead they will save the last line of the standard output to the custom field that is selected.
!!!info
To populate a multiple select custom field, return a string with the options separated by a comma `"This,will,be,an,array"`
For more information See [Collector Tasks](automated_tasks.md#Collector Tasks)

View File

@@ -0,0 +1,9 @@
# Global Key Store
The key store is used to store values that need to be referenced from multiple scripts. This also allows for easy updating of values since scripts reference the values at runtime.
To Add/Edit values in the Global Key Store, browse to **Settings > Global Settings > KeyStore**.
You can reference values from the key store in script arguments by using the {{global.key_name}} syntax.
See [Scripts](scripting.md) for more information.

View File

@@ -0,0 +1,110 @@
# Scripting
Tactical RMM supports uploading existing scripts or adding new scripts right in the dashboard. Languages supported are:
- Powershell
- Windows Batch
- Python
## Adding Scripts
In the dashboard, browse to **Settings > Scripts Manager**. Click the **New** button and select either Upload Script or New Script. The available options for scripts are:
- **Name** - This identifies the script in the dashboard
- **Description** - Optional description for the script
- **Category** - Optional way to group similar scripts together.
- **Type** - This sets the language of the script. Available options are:
- Powershell
- Windows Batch
- Python
- **Script Arguments** - Optional way to set default arguments for scripts. These will autopopulate when running scripts and can be changed at runtime.
- **Default Timeout** - Sets the default timeout of the script and will stop script execution if the duration surpasses the configured timeout. Can be changed at script runtime
- **Favorite** - Favorites the script.
## Downloading Scripts
To download a Tactical RMM Script, click on the script in the Script Manager to select it. Then click the **Download Script** button on the top. You can also right-click on the script and select download
## Community Script
These are script that are built into Tactical RMM. They are provided and mantained by the Tactical RMM community. These scripts are updated whenever Tactical RMM is updated and can't be modified or deleted in the dashboard.
### Hiding Community Scripts
You can choose to hide community script throughout the dashboard by opening **Script Manager** and clicking the **Show/Hide Community Scripts** toggle button.
## Using Scripts
### Manual run on agent
In the **Agent Table**, you can right-click on an agent and select **Run Script**. You have the options of:
- **Wait for Output** - Runs the script and waits for the script to finish running and displays the output.
- **Fire and Forget** - Starts the script and does not wait for output.
- **Email Output** - Starts the script and will email the output. Allows for using the default email address in the global settings or adding a new email address.
There is also an option on the agent context menu called **Run Favorited Script**. This will essentially Fire and Forget the script with default args and timeout.
### Bulk Run on agents
Tactical RMM offers a way to run a script on multiple agents at once. Browse to **Tools > Bulk Script** and select the target for the script to run.
### Automated Tasks
Tactical RMM allows scheduling tasks to run on agents. This leverages the Windows Task Scheduler and has the same scheduling options.
See [Automated Tasks](automated_tasks.md) for configuring automated tasks
### Script Checks
Scripts can also be run periodically on an agent and trigger an alert if it fails.
### Alert Failure/Resolve Actions
Scripts can be triggered when an alert is triggered and resolved. This script will run on any online agent and supports passing the alert information as arguments.
For configuring **Alert Templates**, see [Alerting](../alerting.md)
See below for populating dashboard data in scripts and the available options.
## Using dashboard data in scripts
Tactical RMM allows passing in dashboard data to scripts as arguments. The below powershell arguments will get the client name of the agent and also the agent's public IP address
```
-ClientName {{client.name}} -PublicIP {{agent.public_ip}}
```
!!!info
Script variables are case-sensitive!
See a full list of available options [Here](../script_variables.md)
### Getting Custom Field values
Tactical RMM supports pulling data from custom fields using the {{model.custom_field_name}} syntax.
See [Using Custom Fields in Scripts](custom_fields.md#Using Custom Fields in Scripts)
### Getting values from the Global Keystore
Tactical RMM supports getting values from the global key store using the {{global.key_name}} syntax
See [Global Keystore](keystore.md).
### Example Powershell Script
The below script takes five named values. The arguments will look like this: `-SiteName {{site.name}} -ClientName {{client.name}} -PublicIP {{agent.public_ip}} -CustomField {{client.AV_KEY}} -Global {{global.API_KEY}}`
```powershell
param (
[string] $SiteName,
[string] $ClientName,
[string] $PublicIp,
[string] $CustomField,
[string] $Global
)
Write-Output "Site: $SiteName"
Write-Output "Client: $ClientName"
Write-Output "Public IP: $PublicIp"
Write-Output "Custom Fields: $CustomField"
Write-Output "Global: $Global"
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -10,8 +10,6 @@ It uses an [agent](https://github.com/wh1te909/rmmagent) written in Golang and i
## [LIVE DEMO](https://rmm.tacticalrmm.io/)
*Tactical RMM is currently in alpha and subject to breaking changes. Use in production at your own risk.*
## Features
- Teamviewer-like remote desktop control

View File

@@ -48,18 +48,27 @@ Change offline time on all agents to 5 minutes
python manage.py bulk_change_checkin --offline --all 5
```
Change overdue time on all agents to 10 minutes
```bash
python manage.py bulk_change_checkin --overdue --all 10
```
Change overdue time on all agents in client named *Example Client* to 12 minutes
```bash
python manage.py bulk_change_checkin --overdue --client "Example Client" 12
```
Change offline time on all agents in site named *Example Site* to 2 minutes
```bash
python manage.py bulk_change_checkin --offline --site "Example Site" 2
```
Change offline time on all agents in client named *Example Client* to 12 minutes
```bash
python manage.py bulk_change_checkin --offline --client "Example Client" 12
```
Change overdue time on all agents to 10 minutes
```bash
python manage.py bulk_change_checkin --overdue --all 10
```
Change overdue time on all agents in site named *Example Site* to 4 minutes
```bash
python manage.py bulk_change_checkin --overdue --site "Example Site" 4
```
Change overdue time on all agents in client named *Example Client* to 14 minutes
```bash
python manage.py bulk_change_checkin --overdue --client "Example Client" 14
```

View File

@@ -4,7 +4,9 @@
It is currently not possible to restore to a different domain/subdomain, only to a different physical or virtual server.
!!!danger
You must update your old RMM to the latest version using the `update.sh` script before attempting to restore.
The restore script will always restore to the latest available RMM version on github.
Make sure you update your old RMM to the latest version using the `update.sh` script and then run a fresh backup to use with this restore script.
#### Prepare the new server
Create the same exact linux user account as you did when you installed the original server.

View File

@@ -0,0 +1,39 @@
# Script Variables
Tactical RMM allows passing dashboard data into script as arguments. This uses the syntax `{{client.name}}`.
See below for the available options.
## Agent
- **{{agent.version}}** - Tactical RMM agent version
- **{{agent.operating_system}}** - Agent operating system example: *Windows 10 Pro, 64 bit (build 19042.928)*
- **{{agent.plat}}** - Will show the platform example: *windows*
- **{{agent.hostname}}** - The hostname of the agent
- **{{agent.public_ip}}** - Public IP address of agent
- **{{agent.total_ram}}** - Total RAM on agent. Returns an integer - example: *16*
- **{{agent.boot_time}}** - Uptime of agent. Returns unix timestamp. example: *1619439603.0*
- **{{agent.logged_in_user}}** - Username of logged in user
- **{{agent.monitoring_type}}** - Returns a string of *workstation* or *server*
- **{{agent.description}}** - Description of agent in dashboard
- **{{agent.mesh_node_id}}** - The mesh node id used for linking the tactical agent to mesh.
- **{{agent.choco_installed}}** - Boolean to see if Chocolatey is installed
- **{{agent.patches_last_installed}}** - The date that patches were last installed by Tactical RMM.
- **{{agent.needs_reboot}}** - Returns true if the agent needs a reboot
- **{{agent.time_zone}}** - Returns timezone configured on agent
- **{{agent.maintenance_mode}}** - Returns true if agent is in maintenance mode
## Client
- **{{client.name}}** - Returns name of client
## Site
- **{{site.name}}** - Returns name of Site
## Alert
!!!info
Only available in failure and resolve actions on alert templates!
- **{{alert.alert_time}}** - Time of the alert
- **{{alert.message}}** - Alert message
- **{{alert.severity}}** - Severity of the alert *info, warning, or error*

View File

@@ -12,6 +12,9 @@ nav:
- "Updating the RMM (Docker)": update_docker.md
- "Updating Agents": update_agents.md
- Functionality:
- "Automated Tasks": functions/automated_tasks.md
- "Scripting": functions/scripting.md
- "Global Keystore": functions/keystore.md
- "Custom Fields": functions/custom_fields.md
- "Remote Background": functions/remote_bg.md
- "Maintenance Mode": functions/maintenance_mode.md

View File

@@ -0,0 +1,35 @@
<#
.Synopsis
Defender - Status Report
.DESCRIPTION
This will check Event Log for Windows Defender Malware and Antispyware reports, otherwise will report as Healthy. By default if no command parameter is provided it will check the last 1 day (good for a scheduled daily task).
If a number is provided as a command parameter it will search back that number of days back provided (good for collecting all AV alerts on the computer).
.EXAMPLE
Win_Defender_Status_reports.ps1 365
#>
$param1 = $args[0]
$ErrorActionPreference = 'silentlycontinue'
if ($Args.Count -eq 0) {
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
}
else {
$TimeSpan = (Get-Date) - (New-TimeSpan -Day $param1)
}
if (Get-WinEvent -FilterHashtable @{LogName = 'Microsoft-Windows-Windows Defender/Operational'; ID = '1116', '1118', '1015', '1006', '5010', '5012', '5001', '1123'; StartTime = $TimeSpan }) {
Write-Output "Virus Found or Issue with Defender"
Get-WinEvent -FilterHashtable @{LogName = 'Microsoft-Windows-Windows Defender/Operational'; ID = '1116', '1118', '1015', '1006', '5010', '5012', '5001', '1123'; StartTime = $TimeSpan }
exit 1
}
else {
Write-Output "No Virus Found, Defender is Healthy"
Get-WinEvent -FilterHashtable @{LogName = 'Microsoft-Windows-Windows Defender/Operational'; ID = '1150', '1001'; StartTime = $TimeSpan }
exit 0
}
Exit $LASTEXITCODE

View File

@@ -1,24 +0,0 @@
# This will check for Malware, Antispyware, that Windows Defender is Healthy, last scan etc within the last 24 hours
$ErrorActionPreference= 'silentlycontinue'
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
if (Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Windows Defender/Operational';ID='1116','1118','1015','1006','5010','5012','5001','1123';StartTime=$TimeSpan})
{
Write-Output "Virus Found or Issue with Defender"
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Windows Defender/Operational';ID='1116','1118','1015','1006','5010','5012','5001','1123';StartTime=$TimeSpan}
exit 1
}
else
{
Write-Output "No Virus Found, Defender is Healthy"
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Windows Defender/Operational';ID='1150','1001';StartTime=$TimeSpan}
exit 0
}
Exit $LASTEXITCODE

View File

@@ -1,22 +0,0 @@
# This will check for Malware, Antispyware, that Windows Defender is Healthy, last scan etc within the last 24 hours
$ErrorActionPreference = 'silentlycontinue'
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 365)
if (Get-WinEvent -FilterHashtable @{LogName = 'Microsoft-Windows-Windows Defender/Operational'; ID = '1116', '1118', '1015', '1006', '5010', '5012', '5001', '1123'; StartTime = $TimeSpan })
{
Write-Output "Virus Found or Issue with Defender"
Get-WinEvent -FilterHashtable @{LogName = 'Microsoft-Windows-Windows Defender/Operational'; ID = '1116', '1118', '1015', '1006', '5010', '5012', '5001', '1123'; StartTime = $TimeSpan }
exit 1
}
else
{
Write-Output "No Virus Found, Defender is Healthy"
Get-WinEvent -FilterHashtable @{LogName = 'Microsoft-Windows-Windows Defender/Operational'; ID = '1150', '1001'; StartTime = $TimeSpan }
exit 0
}
Exit $LASTEXITCODE

View File

@@ -0,0 +1,42 @@
# Requires WebClient object $webClient defined, e.g. $webClient = New-Object System.Net.WebClient
#
# Parameters:
# $source - The url of folder to copy, with trailing /, e.g. http://website/folder/structure/
# $destination - The folder to copy $source to, with trailing \ e.g. D:\CopyOfStructure\
# $recursive - True if subfolders of $source are also to be copied or False to ignore subfolders
Function Copy-Folder([string]$source, [string]$destination, [bool]$recursive) {
if (!$(Test-Path($destination))) {
New-Item $destination -type directory -Force
}
# Get the file list from the web page
$webString = $webClient.DownloadString($source)
$lines = [Regex]::Split($webString, "<br>")
# Parse each line, looking for files and folders
foreach ($line in $lines) {
if ($line.ToUpper().Contains("HREF")) {
# File or Folder
if (!$line.ToUpper().Contains("[TO PARENT DIRECTORY]")) {
# Not Parent Folder entry
$items = [Regex]::Split($line, """")
$items = [Regex]::Split($items[2], "(>|<)")
$item = $items[2]
if ($line.ToLower().Contains("&lt;dir&gt")) {
# Folder
if ($recursive) {
# Subfolder copy required
Copy-Folder "$source$item/" "$destination$item/" $recursive
}
else {
# Subfolder copy not required
}
}
else {
# File
$webClient.DownloadFile("$source$item", "$destination$item")
}
}
}
}
}

View File

@@ -0,0 +1,27 @@
Function Install-MSI {
Param (
[Parameter(Mandatory, ValueFromPipeline = $true)]
[ValidateNotNullOrEmpty()]
[System.IO.FileInfo]$File,
[String[]]$AdditionalParams,
[Switch]$OutputLog
)
$DataStamp = get-date -Format yyyyMMddTHHmmss
$logFile = "$($env:programdata)\CentraStage\MilesRMM\{0}-{1}.log" -f $file.fullname, $DataStamp
$MSIArguments = @(
"/i",
('"{0}"' -f $file.fullname),
"/qn",
"/norestart",
"/L*v",
$logFile
)
if ($additionalParams) {
$MSIArguments += $additionalParams
}
Start-Process "msiexec.exe" -ArgumentList $MSIArguments -Wait -NoNewWindow
if ($OutputLog.IsPresent) {
$logContents = get-content $logFile
Write-Output $logContents
}
}

View File

@@ -1,8 +1,8 @@
<#
Requires global variables for serviceName "ScreenConnectService" and url "ScreenConnectInstaller"
Requires global variables for serviceName "ScreenConnectService" and url "ScreenConnectInstaller"'
serviceName is the name of the ScreenConnect Service once it is installed EG: "ScreenConnect Client (1327465grctq84yrtocq)"
url is the path the download the exe version of the ScreenConnect Access installer
Both variables values must start and end with "
url is the path the download the exe version of the ScreenConnect Access installer'
Both variables values must start and end with " (Prior to TRMM Version 0.6.5), remove / don't use " on TRMM Version 0.6.5 or later.
Also accepts uninstall variable to remove the installed instance if required.
#>

View File

@@ -0,0 +1,121 @@
#!/bin/sh
####################################################################################################
#
# Copyright (c) 2017, JAMF Software, LLC. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the JAMF Software, LLC nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY JAMF SOFTWARE, LLC "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL JAMF SOFTWARE, LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
####################################################################################################
#
# ABOUT THIS PROGRAM
#
# NAME
# enableFilewall.sh -- Enables or Disables the firewall on macOS.
#
# SYNOPSIS
# sudo enableFirewall.sh
# sudo enableFirewall.sh <mountPoint> <computerName> <currentUsername> <enableFirewall>
#
# If there is a hardcoded value specified for <enableFirewall> in the script,
# or if the parameter is not passed by Jamf Pro, the hardcoded value in the script will
# be used.
#
# The data that is specified for the <enableFirewall> parameter should be specified in one of
# the following formats. PLEASE NOTE these formats are CASE-SENSITIVE:
#
# "TRUE" or "true" or "YES" or "yes" -> Turn Firewall ON
# "FALSE" or "false" or "NO" or "no" -> Turn Firewall OFF
#
# Example Usage: sudo enableFirewall.sh "mountPoint" "computerName" "currentUsername" "TRUE"
#
# DESCRIPTION
# This script enables or disables the firewall on macOS 10.7 or later.
# It can be used with a hardcoded value in the script, or read in as a parameter.
# Since Jamf Pro defines the first three parameters as (1) Mount Point, (2) Computer
# Name and (3) Username, we are using the fourth parameter ($4) as the passable parameter to
# acquire the status of <enableFirewall>. In addition, the fourth parameter is utilized to set
# the enableFirewall value.
#
####################################################################################################
#
# HISTORY
#
# Version: 1.2
#
# - Created by Nick Amundsen on August 6th, 2008
# - Updated by Nick Amundsen on January 21, 2010
# - Updated by Brandon Wenger on November 27th, 2017
# - Updated by Matthew Mitchell on March 22, 2019
#
####################################################################################################
#
# DEFINE VARIABLES & READ IN PARAMETERS
#
####################################################################################################
# HARDCODED VALUE FOR "enableFirewall" IS SET HERE
enableFirewall=""
# CHECK TO SEE IF A VALUE WAS PASSED IN PARAMETER 4 AND, IF SO, ASSIGN TO "enableFirewall"
if [ "$4" != "" ] && [ "$enableFirewall" == "" ]; then
enableFirewall=$4
fi
####################################################################################################
#
# SCRIPT CONTENTS - DO NOT MODIFY BELOW THIS LINE
#
####################################################################################################
#Check to make sure enableFirewall is not blank
if [ "$enableFirewall" == "" ]; then
echo "Error: The parameter 'enableFirewall' is blank. Please specify a value for parameter 4."
exit 1
fi
#Get the current macOS version (the major release) to check for compatibility
#This will return the 'x' in 10.x
OS=`/usr/bin/defaults read /System/Library/CoreServices/SystemVersion ProductVersion | awk '{print substr($1,1,5)}' | cut -d . -f2`
#If the macOS version is greater than or equal to 10.7
if [[ $OS -ge 7 ]]; then
#Check parameter value, if true or yes, turn the firewall on
case $enableFirewall in "true" | "TRUE" | "yes" | "YES")
echo "Enabling Firewall for macOS 10.$OS ..."
/usr/bin/defaults write /Library/Preferences/com.apple.alf globalstate -int 1;;
#If false or no, turn the firewall off
"false" | "FALSE" | "no" | "NO")
echo "Disabling Firewall for macOS 10.$OS ..."
/usr/bin/defaults write /Library/Preferences/com.apple.alf globalstate -int 0;;
esac
else
#The macOS version is not supported
echo "Unsupported macOS version - 10.7 or later is required."
fi
exit 0;

View File

@@ -0,0 +1 @@
sudo softwareupdate -ia

View File

@@ -0,0 +1,4 @@
networksetup -setdnsservers Wi-Fi 1.1.1.1
networksetup -setdnsservers Wi-Fi 1.0.0.1
networksetup -setdnsservers Ethernet 1.1.1.1
networksetup -setdnsservers Ethernet 1.0.0.1

View File

@@ -0,0 +1,2 @@
pmset -a restoredefaults
nvram -c

View File

@@ -0,0 +1,5 @@
$domain = "myDomain"
$password = "myPassword!" | ConvertTo-SecureString -asPlainText -Force
$username = "$domain\myUserAccount"
$credential = New-Object System.Management.Automation.PSCredential($username,$password)
Add-Computer -DomainName $domain -OUPath "OU=testOU,DC=domain,DC=Domain,DC=com" -Credential $credential -Restart

View File

@@ -0,0 +1,4 @@
# Transfer FSMO Roles to server
# Make this machine the FSMO Master role.
Move-ADDirectoryServerOperationMasterRole -Identity $env:computername -OperationMasterRole pdcemulator,ridmaster,infrastructuremaster,schemamaster,domainnamingmaster -Force

View File

@@ -0,0 +1 @@
manage-bde -protectors C: -get

View File

@@ -0,0 +1,35 @@
## Update this script for your company, Modify the "mail variables" section
## Also, host BlueScreenView.exe on a website and update the $url variable
## location accordingly
##
## Blue Screen View is available as freeware at
## https://www.nirsoft.net/utils/blue_screen_view.html
###script variables
$scriptName = "Blue Screen View"
$computerName = (get-wmiObject win32_computersystem).name
$computerDomain = (get-wmiObject win32_computersystem).domain
if($computerdomain -notlike '*.*'){ #if there's no period in the domain, (workgroup)
$computerDomain = "$computerDomain.local"
}
###mail variables
$smtpServer = 'mail.server.com'
$smtpPort = '25'
$smtpFrom = "Atera-$computername@$computerdomain"
$smtpTo = 'support@YOURDOMAIN.com'
$messageSubject = "Atera Script: $computerName, $scriptName"
$attachment = "c:\windows\temp\crashes.html"
$messageBody += "----See Attachment----"
###script start
$messageBody = "----Blue Screen View Results----`r`n"
$url = "https://YOURDOMAIN.com/files/BlueScreenView.exe"
$filename = "BlueScreenView.exe"
$client = New-Object System.Net.WebClient
$client.DownloadFile($url, "$env:temp\$filename")
Start-Process -FilePath "$env:temp\$filename" -ArgumentList "/shtml","c:\Windows\temp\crashes.html","/sort 2","/sort ~1"""
###send mail
Send-MailMessage -Port $smtpPort -SmtpServer $smtpServer -From $smtpFrom -To $smtpTo -Subject $messageSubject -Body $messageBody -Attachments $attachment

View File

@@ -0,0 +1,61 @@
function Update-ChocoApps {
<#
.SYNOPSIS
Update choco apps and removes the newly created shortcuts.
.DESCRIPTION
Update choco apps and removes the newly created shortcuts.
Requires administrator privileges.
.NOTES
Author: Chris Stafford
Version: 1.0.5
Created: 2020.06.17
Modified: 2020.08.06
#>
# Require Admin Permissions
$IsAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
if ($IsAdmin -eq $false) {
Write-Warning 'Admin Rights Required'
break
}
$StartTime = Get-Date
# Aborts if Chocolatey is not installed
if (Test-Path 'C:\ProgramData\chocolatey\choco.exe') {
# Locations for shortcuts to remove
$Desktops = "$env:PUBLIC\Desktop", "$env:USERPROFILE\Desktop"
$Choco = 'C:\ProgramData\chocolatey\choco.exe'
# Parse outdated app names from choco (leave the space in ' Outdated*')
Write-Output 'Searching for Outdated Apps'
$AppList = & $Choco outdated --limit-output | ForEach-Object { $_.Split('|')[0] }
# Skips if no apps are outdated
if ($AppList.Count -gt 0) {
foreach ($App in $AppList) {
# upgrade app
& $Choco upgrade $App --confirm --limit-output --no-progress
if ($App -like '*.install') {
$App = $App.Split('.')[0]
}
# removes shortcut (created by install) based on the app name and time created
Write-Output "Removing Shortcut: $App"
$Desktops | Get-ChildItem -Filter "*.lnk" -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -gt $StartTime } | Remove-Item
}
}
else {
Write-Output 'No Outdated Apps'
}
}
else {
Write-Output 'Chocolatey is not installed'
}
}
Update-ChocoApps

View File

@@ -0,0 +1,17 @@
ECHO --------------------------------------
ECHO **** Clearing Chrome cache
taskkill /F /IM "chrome.exe">nul 2>&1
set ChromeDataDir="C:\Users\%USERNAME%\AppData\Local\Google\Chrome\User Data\Default"
set ChromeCache=%ChromeDataDir%\Cache>nul 2>&1
del /q /s /f "%ChromeCache%\*.*">nul 2>&1
del /q /f "%ChromeDataDir%\*Cookies*.*">nul 2>&1
del /q /f "%ChromeDataDir%\*History*.*">nul 2>&1
set ChromeDataDir="C:\Users\%USERNAME%\Local Settings\Application Data\Google\Chrome\User Data\Default"
set ChromeCache=%ChromeDataDir%\Cache>nul 2>&1
del /q /s /f "%ChromeCache%\*.*">nul 2>&1
del /q /f "%ChromeDataDir%\*Cookies*.*">nul 2>&1
del /q /f "%ChromeDataDir%\*History*.*">nul 2>&1
ECHO **** Clearing Chrome cache DONE

View File

@@ -0,0 +1,12 @@
taskkill /F /IM "chrome.exe">nul 2>&1
set ChromeDataDir=C:\Users\%USERNAME%\AppData\Local\Google\Chrome\User Data\Default
set ChromeCache=%ChromeDataDir%\Cache>nul 2>&1
del /q /s /f "%ChromeCache%\*.*">nul 2>&1
del /q /f "%ChromeDataDir%\*Cookies*.*">nul 2>&1
del /q /f "%ChromeDataDir%\*History*.*">nul 2>&1
set ChromeDataDir=C:\Users\%USERNAME%\Local Settings\Application Data\Google\Chrome\User Data\Default
set ChromeCache=%ChromeDataDir%\Cache>nul 2>&1
del /q /s /f "%ChromeCache%\*.*">nul 2>&1
del /q /f "%ChromeDataDir%\*Cookies*.*">nul 2>&1
del /q /f "%ChromeDataDir%\*History*.*">nul 2>&1

View File

@@ -0,0 +1,73 @@
#The following variables should be changed:
#$file ? should be named with a .htm ending
#$fromaddress
#$toaddress
#$smtpserver
#$Password
#$port
$file = "C:\Temp\Report.htm"
#HTML Styling
$a = "<style>BODY{font-family: Calibri; font-size: 15pt;}"
$a = $a + "TABLE{border: 1px solid black; border-collapse: collapse;}"
$a = $a + "TH{border: 1px solid green; background: lightgreen; padding: 5px; }"
$a = $a + "TD{border: 1px solid green; padding: 5px; }"
$a = $a + "</style>"
#Heading
"<H1 style='color:green;'>System Report For Agent</H1>" | Out-File $file -Append
#Network Information
Get-WmiObject win32_networkadapterconfiguration -filter "ipenabled = 'True'"|
Select PSComputername, DNSHostName, Description,
@{Name = "IPAddress";Expression =
{[regex]$rx = "(\d{1,3}(\.?)){4}"
$rx.matches($_.IPAddress).Value}},MACAddress | ConvertTo-HTML -Head "<H2 style='color:green;'>Network Information</H2>" -body $a | Out-file $file -Append
#Get Event logs
Get-EventLog -LogName Application -Newest 10 -EntryType Error | Select TimeGenerated, EventID, Source, Message | ConvertTo-HTML -Head "<H2 style='color:green;'>Application Error Event Logs</H2>" -body $a | Out-file $file -Append
Get-EventLog -LogName Application -Newest 10 -EntryType Warning | Select TimeGenerated, EventID, Source, Message | ConvertTo-HTML -Head "<H2 style='color:green;'>Application Warning Event Logs</H2>" -body $a | Out-file $file -Append
Get-EventLog -LogName System -Newest 10 -EntryType Error | Select TimeGenerated, EventID, Source, Message | ConvertTo-HTML -Head "<H2 style='color:green;'>System Error Event Logs</H2>" -body $a | Out-file $file -Append
Get-EventLog -LogName System -Newest 10 -EntryType Warning | Select TimeGenerated, EventID, Source, Message | ConvertTo-HTML -Head "<H2 style='color:green;'>System Warning Event Logs</H2>" -body $a | Out-file $file -Append
#Get Stopped Services
Get-Service | Where {($_.Status) -eq "Stopped"} | Select Status, Name, DisplayName | ConvertTo-HTML -Head "<H2 style='color:green;'>Stopped Services</H2>" -body $a | Out-File $file -Append
#Get Processes and CPU
Get-Process | Select Id, ProcessName, CPU | ConvertTo-HTML -Head "<H2 style='color:green;'>Processes & CPU</H2>" -body $a | Out-File $file -Append
#Get Mapped Drives
Get-PSDrive | Where {$_.Used -ne $null} | Select Name, @{n='Used';e={[float]($_.Used/1GB)}}, @{n='Free';e={[float]($_.Free/1GB)}}, Root| ConvertTo-HTML -Head "<H2 style='color:green;'>Mapped Drives</H2>" -body $a | Out-File $file -Append
#Get Printers
Get-Printer | Select Name, Type, PortName | ConvertTo-HTML -Head "<H2 style='color:green;'>Printers</H2>" -body $a | Out-file $file -append
#Send Email
$fromaddress = "<insert your email address>"
$toaddress = "<insert your email address>"
$Subject = "System Report for Agent"
$body = Get-Content $file
$smtpserver = "<your smtp address>" #for example, smtp.office365.com
$Password = "<insert your email password>"
$port = <insert smtp port> #for example, 587
$message = new-object System.Net.Mail.MailMessage
$message.IsBodyHTML = $true
$message.From = $fromaddress
$message.To.Add($toaddress)
$message.Subject = $Subject
$message.body = $body
$smtp = new-object Net.Mail.SmtpClient($smtpserver, $port)
$smtp.EnableSsl = $true
$smtp.Credentials = New-Object System.Net.NetworkCredential($fromaddress, $Password)
$smtp.Send($message)

View File

@@ -0,0 +1,8 @@
@echo off
rem Get's the MX records for a domain
rem To use a variable instaed of having to put the domain into the script
rem change line 6 to `set domain="\{[DOMAIN]\}" (remove backslashes)
set domain="PUT DOMAIN TO CHECK HERE"
nslookup -type=mx %doamin%

View File

@@ -0,0 +1,20 @@
# Script to Install Windows Defender Application Guard.
# Created by TechCentre with the help and assistance of the internet.
# Restart Required to complete install.
# Sets Variable for feature to be installed.
$FeatureName = "Windows-Defender-ApplicationGuard"
# If Feature Installed already then skips otherwise installs.
if((Get-WindowsOptionalFeature -FeatureName $FeatureName -Online).State -eq "Enabled") {
write-host "Installed"
} else {
write-host "not Installed"
Enable-WindowsOptionalFeature -online -FeatureName $FeatureName -NoRestart
}

View File

@@ -0,0 +1 @@
cleanmgr.exe /AUTOCLEAN

View File

@@ -0,0 +1,16 @@
# Create reg keys
$volumeCaches = Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches"
foreach($key in $volumeCaches)
{
New-ItemProperty -Path "$($key.PSPath)" -Name StateFlags0099 -Value 2 -Type DWORD -Force | Out-Null
}
# Run Disk Cleanup
Start-Process -Wait "$env:SystemRoot\System32\cleanmgr.exe" -ArgumentList "/sagerun:99"
# Delete the keys
$volumeCaches = Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches"
foreach($key in $volumeCaches)
{
Remove-ItemProperty -Path "$($key.PSPath)" -Name StateFlags0099 -Force | Out-Null
}

View File

@@ -0,0 +1,5 @@
DEL /S /Q "%TMP%\*.*"
DEL /S /Q "%TEMP%\*.*"
DEL /S /Q "%WINDIR%\Temp\*.*"
DEL /S /Q "%USERPROFILE%\Local Settings\Temp\*.*"
DEL /S /Q "%LOCALAPPDATA%\Temp\*.*"

View File

@@ -0,0 +1,4 @@
#Update with command parameters
get-ChildItem C:\ -recurse -erroraction silentlycontinue | sort length -descending | select -first 10

View File

@@ -0,0 +1,25 @@
###
# Author: Dave Long <dlong@cagedata.com>
# Gets a list of all mount points and what type of drive the
# mount point is stored on
###
# Get all of the physical disks attached to system
$Partitions = Get-Partition | Where-Object { [string]($_.DriveLetter) -ne "" }
$Output = @()
$Partitions | ForEach-Object {
$Disk = Get-PhysicalDisk -DeviceNumber $_.DiskNumber
$Output += [PSCustomObject]@{
MountPoint = $_.DriveLetter
DiskType = $Disk.MediaType
DriveName = $Disk.FriendlyName
DriveSerialNumber = $Disk.SerialNumber
SizeInGigabytes = $Disk.Size/1GB
Health = $Disk.HealthStatus
SystemDrive = $env:SystemDrive[0] -eq $_.DriveLetter ? $true : $false
}
}
$Output | Format-Table

View File

@@ -0,0 +1,15 @@
@echo off
for /F %%a IN (?wevtutil el?) DO (wevtutil.exe cl %%a >nul 2>&1)
IF (%adminTest%)==(Access) goto noAdmin
for /F "tokens=*" %%G in ('wevtutil.exe el') DO (call :do_clear "%%G")
echo.
echo Event Logs have been cleared!
goto theEnd
:do_clear
echo clearing %1
wevtutil.exe cl %1
goto :eof
:noAdmin
echo You must run this script as an Administrator!
echo.
:theEnd

View File

@@ -0,0 +1 @@
Wevtutil.exe cl Application

View File

@@ -0,0 +1 @@
Wevtutil.exe cl System

View File

@@ -0,0 +1,53 @@
<#
.Synopsis
Detect if object exists and gives error
.DESCRIPTION
Long description
.EXAMPLE
Example of how to use this cmdlet
.EXAMPLE
Another example of how to use this cmdlet
#>
If ((Test-Path -Path "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Tactical RMM Agent.lnk" -PathType Leaf) -eq $false ) {
Write-Output "No Shortcut"
exit 0
}
Else {
Write-Output 'Shortcut Exists'
exit 1
}
Exit $LASTEXITCODE
# function Verb-Noun
# {
# [CmdletBinding()]
# [Alias()]
# [OutputType([int])]
# Param
# (
# # Param1 help description
# [Parameter(Mandatory=$true,
# ValueFromPipelineByPropertyName=$true,
# Position=0)]
# $Param1,
# # Param2 help description
# [int]
# $Param2
# )
# Begin
# {
# }
# Process
# {
# }
# End
# {
# }
# }

View File

@@ -0,0 +1 @@
netsh advfirewall set allprofiles state off

View File

@@ -0,0 +1,7 @@
$root="c:\users"
$users=get-childitem -path $root -exclude administrator, public
foreach ($user in $users)
{
$folder= join-path -path $user -childpath "downloads\*"
Get-childitem $folder -recurse | remove-item -force
}

View File

@@ -0,0 +1 @@
NET USER %username% /DOMAIN | FIND /I "Password last set"

View File

@@ -0,0 +1,6 @@
#Find last reboot information
gwmi win32_ntlogevent -filter "LogFile='System' and EventCode='1074' and Message like '%restart%'" |
select User,@{n="Time";e={$_.ConvertToDateTime($_.TimeGenerated)}}

View File

@@ -0,0 +1 @@
powercfg /batteryreport /output "C:\battery-report.html"

View File

@@ -0,0 +1,37 @@
$RegKeyPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\PersonalizationCSP"
$DesktopPath = "DesktopImagePath"
$DesktopStatus = "DesktopImageStatus"
$DesktopUrl = "DesktopImageUrl"
$LockScreenPath = "LockScreenImagePath"
$LockScreenStatus = "LockScreenImageStatus"
$LockScreenUrl = "LockScreenImageUrl"
$StatusValue = "1"
$DesktopImageValue = "C:\Lakes\Desktop.jpg" #Change as per your needs
$LockScreenImageValue = "C:\Lakes\LockScreen.jpg" #Change as per your needs
IF(!(Test-Path $RegKeyPath))
{
New-Item -Path $RegKeyPath -Force | Out-Null
New-ItemProperty -Path $RegKeyPath -Name $DesktopStatus -Value $StatusValue -PropertyType DWORD -Force | Out-Null
New-ItemProperty -Path $RegKeyPath -Name $LockScreenStatus -Value $StatusValue -PropertyType DWORD -Force | Out-Null
New-ItemProperty -Path $RegKeyPath -Name $DesktopPath -Value $DesktopImageValue -PropertyType STRING -Force | Out-Null
New-ItemProperty -Path $RegKeyPath -Name $DesktopUrl -Value $DesktopImageValue -PropertyType STRING -Force | Out-Null
New-ItemProperty -Path $RegKeyPath -Name $LockScreenPath -Value $LockScreenImageValue -PropertyType STRING -Force | Out-Null
New-ItemProperty -Path $RegKeyPath -Name $LockScreenUrl -Value $LockScreenImageValue -PropertyType STRING -Force | Out-Null
}
ELSE {
New-ItemProperty -Path $RegKeyPath -Name $DesktopStatus -Value $Statusvalue -PropertyType DWORD -Force | Out-Null
New-ItemProperty -Path $RegKeyPath -Name $LockScreenStatus -Value $value -PropertyType DWORD -Force | Out-Null
New-ItemProperty -Path $RegKeyPath -Name $DesktopPath -Value $DesktopImageValue -PropertyType STRING -Force | Out-Null
New-ItemProperty -Path $RegKeyPath -Name $DesktopUrl -Value $DesktopImageValue -PropertyType STRING -Force | Out-Null
New-ItemProperty -Path $RegKeyPath -Name $LockScreenPath -Value $LockScreenImageValue -PropertyType STRING -Force | Out-Null
New-ItemProperty -Path $RegKeyPath -Name $LockScreenUrl -Value $LockScreenImageValue -PropertyType STRING -Force | Out-Null
}

View File

@@ -0,0 +1,71 @@
# Define the Variables 1-3
# 1. Enter the beginning of the time range being reviewed. Use the same time format as configured in the endpoint's time & date settings (for example, for USA date&time: MM-DD-YYY hh:mm:ss).
$StartTime = "12-01-2017 17:00:00"
# 2. Enter the end of the time range being reviewed. Use the same time format as configured in the endpoint's time & date settings (for example, for USA date&time: MM-DD-YYY hh:mm:ss).
$EndTime = "12-14-2017 17:00:00"
# 3. Location of the result file. Make sure the file type is csv.
$ResultFile = "C:\Temp\LoginAttemptsResultFile.csv"
# Create the output file and define the column headers.
"Time Created, Domain\Username, Login Attempt" | Add-Content $ResultFile
# Query the server for the login events.
$colEvents = Get-WinEvent -FilterHashtable @{logname='Security'; StartTime="$StartTime"; EndTime="$EndTime"}
# Iterate through the collection of login events.
Foreach ($Entry in $colEvents)
{
If (($Entry.Id -eq "4624") -and ($Entry.Properties[8].value -eq "2"))
{
$TimeCreated = $Entry.TimeCreated
$Domain = $Entry.Properties[6].Value
$Username = $Entry.Properties[5].Value
$Result = "$TimeCreated,$Domain\$Username,Interactive Login Success" | Add-Content $ResultFile
}
If (($Entry.Id -eq "4624") -and ($Entry.Properties[8].value -eq "10"))
{
$TimeCreated = $Entry.TimeCreated
$Domain = $Entry.Properties[6].Value
$Username = $Entry.Properties[5].Value
$Result = "$TimeCreated,$Domain\$Username,Remote Login Success" | Add-Content $ResultFile
}
If ($Entry.Id -eq "4625")
{
$TimeCreated = $Entry.TimeCreated
$Domain = $Entry.Properties[6].Value
$Username = $Entry.Properties[5].Value
$Result = "$TimeCreated,$Domain\$Username,Login Failure" | Add-Content $ResultFile
}
}

View File

@@ -0,0 +1,2 @@
REG ADD "HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\Common\General" /f /v PreferCloudSaveLocations /t REG_DWORD /d 0
REG ADD "HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\Common\Internet" /f /v OnlineStorage /t REG_DWORD /d 3

View File

@@ -0,0 +1,93 @@
echo OFF
cls
:: Check for MS SQL Server Versions
set CURRENT_VERSION=nul
echo.
FOR /F "tokens=3 skip=2" %%i IN ('REG QUERY "HKLM\SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\CurrentVersion" /v CurrentVersion 2^>nul') DO set CURRENT_VERSION=%%i
if defined CURRENT_VERSION (
:: MS SQL Server 2019 Versions
if %CURRENT_VERSION% equ 15.0.2000.5 set SQL_NAME=Microsoft SQL Server 2019
:: MS SQL Server 2017 Versions
if %CURRENT_VERSION% equ 14.0.1000.169 set SQL_NAME=Microsoft SQL Server 2017
:: MS SQL Server 2016 Versions
if %CURRENT_VERSION% equ 13.0.5026.0 set SQL_NAME=Microsoft SQL Server 2016 SP2
if %CURRENT_VERSION% equ 13.0.4001.0 set SQL_NAME=Microsoft SQL Server 2016 SP1
if %CURRENT_VERSION% equ 13.0.1601.5 set SQL_NAME=Microsoft SQL Server 2016
:: MS SQL Server 2014 Versions
if %CURRENT_VERSION% equ 12.0.6024.1 set SQL_NAME=Microsoft SQL Server 2014 SP3
if %CURRENT_VERSION% equ 12.0.5000.0 set SQL_NAME=Microsoft SQL Server 2014 SP2
if %CURRENT_VERSION% equ 12.0.4100.1 set SQL_NAME=Microsoft SQL Server 2014 SP1
if %CURRENT_VERSION% equ 12.0.2000.8 set SQL_NAME=Microsoft SQL Server 2014
:: MS SQL Server 2012 Versions
if %CURRENT_VERSION% equ 11.0.7001.0 set SQL_NAME=Microsoft SQL Server 2012 SP4
if %CURRENT_VERSION% equ 11.0.6020.0 set SQL_NAME=Microsoft SQL Server 2012 SP3
if %CURRENT_VERSION% equ 11.0.5058.0 set SQL_NAME=Microsoft SQL Server 2012 SP2
if %CURRENT_VERSION% equ 11.0.3000.0 set SQL_NAME=Microsoft SQL Server 2012 SP1
if %CURRENT_VERSION% equ 11.0.2100.60 set SQL_NAME=Microsoft SQL Server 2012
:: MS SQL Server 2008 R2 Versions
if %CURRENT_VERSION% equ 10.50.6000.34 set SQL_NAME=Microsoft SQL Server 2008 R2 SP3
if %CURRENT_VERSION% equ 10.50.4000.0 set SQL_NAME=Microsoft SQL Server 2008 R2 SP2
if %CURRENT_VERSION% equ 10.50.2500.0 set SQL_NAME=Microsoft SQL Server 2008 R2 SP1
if %CURRENT_VERSION% equ 10.50.1600.1 set SQL_NAME=Microsoft SQL Server 2008 R2
:: MS SQL Server 2008 Versions
if %CURRENT_VERSION% equ 10.0.6000.29 set SQL_NAME=Microsoft SQL Server 2008 SP4
if %CURRENT_VERSION% equ 10.0.5000.0 set SQL_NAME=Microsoft SQL Server 2008 SP3
if %CURRENT_VERSION% equ 10.0.4000.0 set SQL_NAME=Microsoft SQL Server 2008 SP2
if %CURRENT_VERSION% equ 10.0.2531.0 set SQL_NAME=Microsoft SQL Server 2008 SP1
if %CURRENT_VERSION% equ 10.0.1600.22 set SQL_NAME=Microsoft SQL Server 2008
)
if %CURRENT_VERSION% equ nul (
echo No Microsoft SQL Server found/installed!
) else (
echo Installed Microsoft SQL Server Release:
echo %SQL_NAME% [%CURRENT_VERSION%]
)
:: Check for MS SQL Server Express Versions
set CURRENT_VERSION=nul
echo.
FOR /F "tokens=3 skip=2" %%i IN ('REG QUERY "HKLM\SOFTWARE\Microsoft\Microsoft SQL Server\SQLEXPRESS\MSSQLServer\CurrentVersion" /v CurrentVersion 2^>nul') DO set CURRENT_VERSION=%%i
if defined CURRENT_VERSION (
:: MS SQL Server 2017 Express Versions
if %CURRENT_VERSION% equ 14.0.1000.169 set SQL_NAME=Microsoft SQL Server 2017 Express
:: MS SQL Server 2016 Express Versions
if %CURRENT_VERSION% equ 13.0.5026.0 set SQL_NAME=Microsoft SQL Server 2016 Express SP2
if %CURRENT_VERSION% equ 13.0.4001.0 set SQL_NAME=Microsoft SQL Server 2016 Express SP1
if %CURRENT_VERSION% equ 13.0.1601.5 set SQL_NAME=Microsoft SQL Server 2016 Express
:: MS SQL Server 2014 Express Versions
if %CURRENT_VERSION% equ 12.0.6024.1 set SQL_NAME=Microsoft SQL Server 2014 Express SP3
if %CURRENT_VERSION% equ 12.0.5000.0 set SQL_NAME=Microsoft SQL Server 2014 Express SP2
if %CURRENT_VERSION% equ 12.0.4100.1 set SQL_NAME=Microsoft SQL Server 2014 Express SP1
if %CURRENT_VERSION% equ 12.0.2000.8 set SQL_NAME=Microsoft SQL Server 2014 Express
:: MS SQL Server 2012 Express Versions
if %CURRENT_VERSION% equ 11.0.7001.0 set SQL_NAME=Microsoft SQL Server 2012 Express SP4
if %CURRENT_VERSION% equ 11.0.6020.0 set SQL_NAME=Microsoft SQL Server 2012 Express SP3
if %CURRENT_VERSION% equ 11.0.5058.0 set SQL_NAME=Microsoft SQL Server 2012 Express SP2
if %CURRENT_VERSION% equ 11.0.3000.0 set SQL_NAME=Microsoft SQL Server 2012 Express SP1
if %CURRENT_VERSION% equ 11.0.2100.60 set SQL_NAME=Microsoft SQL Server 2012 Express
:: MS SQL Server 2008 R2 Express Versions
if %CURRENT_VERSION% equ 10.50.6000.34 set SQL_NAME=Microsoft SQL Server 2008 R2 Express SP3
if %CURRENT_VERSION% equ 10.50.4000.0 set SQL_NAME=Microsoft SQL Server 2008 R2 Express SP2
if %CURRENT_VERSION% equ 10.50.2500.0 set SQL_NAME=Microsoft SQL Server 2008 R2 Express SP1
if %CURRENT_VERSION% equ 10.50.1600.1 set SQL_NAME=Microsoft SQL Server 2008 R2 Express
:: MS SQL Server 2008 Express Versions
if %CURRENT_VERSION% equ 10.0.6000.29 set SQL_NAME=Microsoft SQL Server 2008 Express SP4
if %CURRENT_VERSION% equ 10.0.5000.0 set SQL_NAME=Microsoft SQL Server 2008 Express SP3
if %CURRENT_VERSION% equ 10.0.4000.0 set SQL_NAME=Microsoft SQL Server 2008 Express SP2
if %CURRENT_VERSION% equ 10.0.2531.0 set SQL_NAME=Microsoft SQL Server 2008 Express SP1
if %CURRENT_VERSION% equ 10.0.1600.22 set SQL_NAME=Microsoft SQL Server 2008 Express
)
if %CURRENT_VERSION% equ nul (
echo No Microsoft SQL Server Express found/installed!
) else (
echo Installed Microsoft SQL Server Express Release:
echo %SQL_NAME% [%CURRENT_VERSION%]
)
echo.

View File

@@ -0,0 +1 @@
IPCONFIG /FLUSHDNS

Some files were not shown because too many files have changed in this diff Show More