Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
715982e40a | ||
|
|
d00cd4453a | ||
|
|
429c08c24a | ||
|
|
6a71490e20 | ||
|
|
9bceda0646 | ||
|
|
a1027a6773 | ||
|
|
302d4b75f9 | ||
|
|
5f6ee0e883 | ||
|
|
27f9720de1 | ||
|
|
22aa3fdbbc | ||
|
|
069ecdd33f | ||
|
|
dd545ae933 | ||
|
|
6650b705c4 | ||
|
|
59b0350289 | ||
|
|
1ad159f820 | ||
|
|
0bf42190e9 | ||
|
|
d2fa836232 | ||
|
|
c387774093 | ||
|
|
e99736ba3c | ||
|
|
16cb54fcc9 | ||
|
|
5aa15c51ec | ||
|
|
a8aedd9cf3 | ||
|
|
b851b632bc | ||
|
|
541e07fb65 | ||
|
|
6ad16a897d | ||
|
|
72f1053a93 | ||
|
|
fb15a2762c | ||
|
|
9165248b91 | ||
|
|
add18b29db | ||
|
|
1971653548 | ||
|
|
392cd64d7b | ||
|
|
b5affbb7c8 | ||
|
|
71d1206277 | ||
|
|
26e6a8c409 | ||
|
|
eb54fae11a | ||
|
|
ee773e5966 | ||
|
|
7218ccdba8 | ||
|
|
332400e48a | ||
|
|
ad1a5d3702 | ||
|
|
3006b4184d | ||
|
|
84eb84a080 | ||
|
|
60beea548b | ||
|
|
5f9c149e59 | ||
|
|
53367c6f04 | ||
|
|
d7f817ee44 | ||
|
|
d33a87da54 | ||
|
|
3aebfb12b7 | ||
|
|
1d6c55ffa6 | ||
|
|
5e7080aac3 | ||
|
|
fad739bc01 | ||
|
|
c6b7f23884 | ||
|
|
a6f7e446de |
@@ -115,7 +115,10 @@ services:
|
||||
redis-dev:
|
||||
container_name: trmm-redis-dev
|
||||
restart: always
|
||||
command: redis-server --appendonly yes
|
||||
image: redis:6.0-alpine
|
||||
volumes:
|
||||
- redis-data-dev:/data
|
||||
networks:
|
||||
dev:
|
||||
aliases:
|
||||
@@ -247,6 +250,7 @@ volumes:
|
||||
postgres-data-dev:
|
||||
mongo-dev-data:
|
||||
mesh-data-dev:
|
||||
redis-data-dev:
|
||||
|
||||
networks:
|
||||
dev:
|
||||
|
||||
@@ -211,6 +211,7 @@ def agent_outages_task() -> None:
|
||||
|
||||
agents = Agent.objects.only(
|
||||
"pk",
|
||||
"agent_id",
|
||||
"last_seen",
|
||||
"offline_time",
|
||||
"overdue_time",
|
||||
|
||||
@@ -321,11 +321,16 @@ class CheckRunner(APIView):
|
||||
|
||||
def patch(self, request):
|
||||
check = get_object_or_404(Check, pk=request.data["id"])
|
||||
if pyver.parse(check.agent.version) < pyver.parse("1.5.7"):
|
||||
return notify_error("unsupported")
|
||||
|
||||
check.last_run = djangotime.now()
|
||||
check.save(update_fields=["last_run"])
|
||||
status = check.handle_checkv2(request.data)
|
||||
status = check.handle_check(request.data)
|
||||
if status == "failing" and check.assignedtask.exists(): # type: ignore
|
||||
check.handle_assigned_task()
|
||||
|
||||
return Response(status)
|
||||
return Response("ok")
|
||||
|
||||
|
||||
class CheckRunnerInterval(APIView):
|
||||
@@ -378,9 +383,18 @@ class TaskRunner(APIView):
|
||||
)
|
||||
|
||||
# get last line of stdout
|
||||
value = new_task.stdout.split("\n")[-1].strip()
|
||||
value = (
|
||||
new_task.stdout
|
||||
if task.collector_all_output
|
||||
else new_task.stdout.split("\n")[-1].strip()
|
||||
)
|
||||
|
||||
if task.custom_field.type in ["text", "number", "single", "datetime"]:
|
||||
if task.custom_field.type in [
|
||||
"text",
|
||||
"number",
|
||||
"single",
|
||||
"datetime",
|
||||
]:
|
||||
agent_field.string_value = value
|
||||
agent_field.save()
|
||||
elif task.custom_field.type == "multiple":
|
||||
|
||||
@@ -3,7 +3,7 @@ from typing import Any, Dict, List, Union
|
||||
from tacticalrmm.celery import app
|
||||
|
||||
|
||||
@app.task
|
||||
@app.task(retry_backoff=5, retry_jitter=True, retry_kwargs={"max_retries": 5})
|
||||
def generate_agent_checks_task(
|
||||
policy: int = None,
|
||||
site: int = None,
|
||||
@@ -57,7 +57,9 @@ def generate_agent_checks_task(
|
||||
return "ok"
|
||||
|
||||
|
||||
@app.task
|
||||
@app.task(
|
||||
acks_late=True, retry_backoff=5, retry_jitter=True, retry_kwargs={"max_retries": 5}
|
||||
)
|
||||
# updates policy managed check fields on agents
|
||||
def update_policy_check_fields_task(check: int) -> str:
|
||||
from checks.models import Check
|
||||
@@ -73,7 +75,7 @@ def update_policy_check_fields_task(check: int) -> str:
|
||||
return "ok"
|
||||
|
||||
|
||||
@app.task
|
||||
@app.task(retry_backoff=5, retry_jitter=True, retry_kwargs={"max_retries": 5})
|
||||
# generates policy tasks on agents affected by a policy
|
||||
def generate_agent_autotasks_task(policy: int = None) -> str:
|
||||
from agents.models import Agent
|
||||
@@ -100,7 +102,12 @@ def generate_agent_autotasks_task(policy: int = None) -> str:
|
||||
return "ok"
|
||||
|
||||
|
||||
@app.task
|
||||
@app.task(
|
||||
acks_late=True,
|
||||
retry_backoff=5,
|
||||
retry_jitter=True,
|
||||
retry_kwargs={"max_retries": 5},
|
||||
)
|
||||
def delete_policy_autotasks_task(task: int) -> str:
|
||||
from autotasks.models import AutomatedTask
|
||||
|
||||
@@ -120,7 +127,12 @@ def run_win_policy_autotasks_task(task: int) -> str:
|
||||
return "ok"
|
||||
|
||||
|
||||
@app.task
|
||||
@app.task(
|
||||
acks_late=True,
|
||||
retry_backoff=5,
|
||||
retry_jitter=True,
|
||||
retry_kwargs={"max_retries": 5},
|
||||
)
|
||||
def update_policy_autotasks_fields_task(task: int, update_agent: bool = False) -> str:
|
||||
from autotasks.models import AutomatedTask
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.2.1 on 2021-05-29 03:26
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('autotasks', '0021_alter_automatedtask_custom_field'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='automatedtask',
|
||||
name='collector_all_output',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
||||
@@ -10,6 +10,7 @@ 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 django.db.utils import DatabaseError
|
||||
from django.utils import timezone as djangotime
|
||||
from logs.models import BaseAuditModel
|
||||
from loguru import logger
|
||||
@@ -104,6 +105,7 @@ class AutomatedTask(BaseAuditModel):
|
||||
task_type = models.CharField(
|
||||
max_length=100, choices=TASK_TYPE_CHOICES, default="manual"
|
||||
)
|
||||
collector_all_output = models.BooleanField(default=False)
|
||||
run_time_date = DateTimeField(null=True, blank=True)
|
||||
remove_if_not_scheduled = models.BooleanField(default=False)
|
||||
run_asap_after_missed = models.BooleanField(default=False) # added in agent v1.4.7
|
||||
@@ -182,6 +184,7 @@ class AutomatedTask(BaseAuditModel):
|
||||
"remove_if_not_scheduled",
|
||||
"run_asap_after_missed",
|
||||
"custom_field",
|
||||
"collector_all_output",
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
@@ -363,9 +366,14 @@ class AutomatedTask(BaseAuditModel):
|
||||
|
||||
if r != "ok" and "The system cannot find the file specified" not in r:
|
||||
self.sync_status = "pendingdeletion"
|
||||
self.save(update_fields=["sync_status"])
|
||||
|
||||
try:
|
||||
self.save(update_fields=["sync_status"])
|
||||
except DatabaseError:
|
||||
pass
|
||||
|
||||
logger.warning(
|
||||
f"{agent.hostname} task {self.name} was successfully modified"
|
||||
f"{agent.hostname} task {self.name} will be deleted on next checkin"
|
||||
)
|
||||
return "timeout"
|
||||
else:
|
||||
|
||||
22
api/tacticalrmm/checks/migrations/0024_auto_20210606_1632.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# Generated by Django 3.2.1 on 2021-06-06 16:32
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('checks', '0023_check_run_interval'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='checkhistory',
|
||||
name='check_history',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='checkhistory',
|
||||
name='check_id',
|
||||
field=models.PositiveIntegerField(default=0),
|
||||
),
|
||||
]
|
||||
@@ -1,4 +1,3 @@
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import string
|
||||
@@ -14,9 +13,7 @@ from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
from django.db import models
|
||||
from logs.models import BaseAuditModel
|
||||
from loguru import logger
|
||||
from packaging import version as pyver
|
||||
|
||||
from .utils import bytes2human
|
||||
|
||||
logger.configure(**settings.LOG_CONFIG)
|
||||
|
||||
@@ -316,9 +313,9 @@ class Check(BaseAuditModel):
|
||||
)
|
||||
|
||||
def add_check_history(self, value: int, more_info: Any = None) -> None:
|
||||
CheckHistory.objects.create(check_history=self, y=value, results=more_info)
|
||||
CheckHistory.objects.create(check_id=self.pk, y=value, results=more_info)
|
||||
|
||||
def handle_checkv2(self, data):
|
||||
def handle_check(self, data):
|
||||
from alerts.models import Alert
|
||||
|
||||
# cpuload or mem checks
|
||||
@@ -349,9 +346,6 @@ class Check(BaseAuditModel):
|
||||
elif self.check_type == "diskspace":
|
||||
if data["exists"]:
|
||||
percent_used = round(data["percent_used"])
|
||||
total = bytes2human(data["total"])
|
||||
free = bytes2human(data["free"])
|
||||
|
||||
if self.error_threshold and (100 - percent_used) < self.error_threshold:
|
||||
self.status = "failing"
|
||||
self.alert_severity = "error"
|
||||
@@ -365,7 +359,7 @@ class Check(BaseAuditModel):
|
||||
else:
|
||||
self.status = "passing"
|
||||
|
||||
self.more_info = f"Total: {total}B, Free: {free}B"
|
||||
self.more_info = data["more_info"]
|
||||
|
||||
# add check history
|
||||
self.add_check_history(100 - percent_used)
|
||||
@@ -381,12 +375,7 @@ class Check(BaseAuditModel):
|
||||
self.stdout = data["stdout"]
|
||||
self.stderr = data["stderr"]
|
||||
self.retcode = data["retcode"]
|
||||
try:
|
||||
# python agent
|
||||
self.execution_time = "{:.4f}".format(data["stop"] - data["start"])
|
||||
except:
|
||||
# golang agent
|
||||
self.execution_time = "{:.4f}".format(data["runtime"])
|
||||
self.execution_time = "{:.4f}".format(data["runtime"])
|
||||
|
||||
if data["retcode"] in self.info_return_codes:
|
||||
self.alert_severity = "info"
|
||||
@@ -422,22 +411,8 @@ class Check(BaseAuditModel):
|
||||
|
||||
# ping checks
|
||||
elif self.check_type == "ping":
|
||||
output = data["output"]
|
||||
|
||||
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"
|
||||
else:
|
||||
self.status = data["status"]
|
||||
|
||||
self.more_info = output
|
||||
self.status = data["status"]
|
||||
self.more_info = data["output"]
|
||||
self.save(update_fields=["more_info"])
|
||||
|
||||
self.add_check_history(
|
||||
@@ -446,41 +421,8 @@ class Check(BaseAuditModel):
|
||||
|
||||
# windows service checks
|
||||
elif self.check_type == "winsvc":
|
||||
svc_stat = data["status"]
|
||||
self.more_info = f"Status {svc_stat.upper()}"
|
||||
|
||||
if data["exists"]:
|
||||
if svc_stat == "running":
|
||||
self.status = "passing"
|
||||
elif svc_stat == "start_pending" and self.pass_if_start_pending:
|
||||
self.status = "passing"
|
||||
else:
|
||||
if self.agent and self.restart_if_stopped:
|
||||
nats_data = {
|
||||
"func": "winsvcaction",
|
||||
"payload": {"name": self.svc_name, "action": "start"},
|
||||
}
|
||||
r = asyncio.run(self.agent.nats_cmd(nats_data, timeout=32))
|
||||
if r == "timeout" or r == "natsdown":
|
||||
self.status = "failing"
|
||||
elif not r["success"] and r["errormsg"]:
|
||||
self.status = "failing"
|
||||
elif r["success"]:
|
||||
self.status = "passing"
|
||||
self.more_info = f"Status RUNNING"
|
||||
else:
|
||||
self.status = "failing"
|
||||
else:
|
||||
self.status = "failing"
|
||||
|
||||
else:
|
||||
if self.pass_if_svc_not_exist:
|
||||
self.status = "passing"
|
||||
else:
|
||||
self.status = "failing"
|
||||
|
||||
self.more_info = f"Service {self.svc_name} does not exist"
|
||||
|
||||
self.status = data["status"]
|
||||
self.more_info = data["more_info"]
|
||||
self.save(update_fields=["more_info"])
|
||||
|
||||
self.add_check_history(
|
||||
@@ -488,49 +430,7 @@ class Check(BaseAuditModel):
|
||||
)
|
||||
|
||||
elif self.check_type == "eventlog":
|
||||
log = []
|
||||
is_wildcard = self.event_id_is_wildcard
|
||||
eventType = self.event_type
|
||||
eventID = self.event_id
|
||||
source = self.event_source
|
||||
message = self.event_message
|
||||
r = data["log"]
|
||||
|
||||
for i in r:
|
||||
if i["eventType"] == eventType:
|
||||
if not is_wildcard and not int(i["eventID"]) == eventID:
|
||||
continue
|
||||
|
||||
if not source and not message:
|
||||
if is_wildcard:
|
||||
log.append(i)
|
||||
elif int(i["eventID"]) == eventID:
|
||||
log.append(i)
|
||||
continue
|
||||
|
||||
if source and message:
|
||||
if is_wildcard:
|
||||
if source in i["source"] and message in i["message"]:
|
||||
log.append(i)
|
||||
|
||||
elif int(i["eventID"]) == eventID:
|
||||
if source in i["source"] and message in i["message"]:
|
||||
log.append(i)
|
||||
|
||||
continue
|
||||
|
||||
if source and source in i["source"]:
|
||||
if is_wildcard:
|
||||
log.append(i)
|
||||
elif int(i["eventID"]) == eventID:
|
||||
log.append(i)
|
||||
|
||||
if message and message in i["message"]:
|
||||
if is_wildcard:
|
||||
log.append(i)
|
||||
elif int(i["eventID"]) == eventID:
|
||||
log.append(i)
|
||||
|
||||
log = data["log"]
|
||||
if self.fail_when == "contains":
|
||||
if log and len(log) >= self.number_of_events_b4_alert:
|
||||
self.status = "failing"
|
||||
@@ -567,6 +467,11 @@ class Check(BaseAuditModel):
|
||||
|
||||
return self.status
|
||||
|
||||
def handle_assigned_task(self) -> None:
|
||||
for task in self.assignedtask.all(): # type: ignore
|
||||
if task.enabled:
|
||||
task.run_win_task()
|
||||
|
||||
@staticmethod
|
||||
def serialize(check):
|
||||
# serializes the check and returns json
|
||||
@@ -604,7 +509,12 @@ class Check(BaseAuditModel):
|
||||
)
|
||||
|
||||
for task in self.assignedtask.all(): # type: ignore
|
||||
task.create_policy_task(agent=agent, policy=policy, assigned_check=check)
|
||||
if policy or (
|
||||
agent and not agent.autotasks.filter(parent_task=task.pk).exists()
|
||||
):
|
||||
task.create_policy_task(
|
||||
agent=agent, policy=policy, assigned_check=check
|
||||
)
|
||||
|
||||
for field in self.policy_fields_to_copy:
|
||||
setattr(check, field, getattr(self, field))
|
||||
@@ -778,14 +688,10 @@ class Check(BaseAuditModel):
|
||||
|
||||
|
||||
class CheckHistory(models.Model):
|
||||
check_history = models.ForeignKey(
|
||||
Check,
|
||||
related_name="check_history",
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
check_id = models.PositiveIntegerField(default=0)
|
||||
x = models.DateTimeField(auto_now_add=True)
|
||||
y = models.PositiveIntegerField(null=True, blank=True, default=None)
|
||||
results = models.JSONField(null=True, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.check_history.readable_desc
|
||||
return self.x
|
||||
|
||||
@@ -158,14 +158,8 @@ class AssignedTaskCheckRunnerField(serializers.ModelSerializer):
|
||||
|
||||
class CheckRunnerGetSerializer(serializers.ModelSerializer):
|
||||
# only send data needed for agent to run a check
|
||||
assigned_tasks = serializers.SerializerMethodField()
|
||||
script = ScriptCheckSerializer(read_only=True)
|
||||
|
||||
def get_assigned_tasks(self, obj):
|
||||
if obj.assignedtask.exists():
|
||||
tasks = obj.assignedtask.all()
|
||||
return AssignedTaskCheckRunnerField(tasks, many=True).data
|
||||
|
||||
class Meta:
|
||||
model = Check
|
||||
exclude = [
|
||||
@@ -193,6 +187,7 @@ class CheckRunnerGetSerializer(serializers.ModelSerializer):
|
||||
"modified_by",
|
||||
"modified_time",
|
||||
"history",
|
||||
"dashboard_alert",
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -363,10 +363,10 @@ class TestCheckViews(TacticalTestCase):
|
||||
# setup data
|
||||
agent = baker.make_recipe("agents.agent")
|
||||
check = baker.make_recipe("checks.diskspace_check", agent=agent)
|
||||
baker.make("checks.CheckHistory", check_history=check, _quantity=30)
|
||||
baker.make("checks.CheckHistory", check_id=check.id, _quantity=30)
|
||||
check_history_data = baker.make(
|
||||
"checks.CheckHistory",
|
||||
check_history=check,
|
||||
check_id=check.id,
|
||||
_quantity=30,
|
||||
)
|
||||
|
||||
@@ -400,17 +400,17 @@ class TestCheckTasks(TacticalTestCase):
|
||||
def setUp(self):
|
||||
self.authenticate()
|
||||
self.setup_coresettings()
|
||||
self.agent = baker.make_recipe("agents.agent")
|
||||
self.agent = baker.make_recipe("agents.agent", version="1.5.7")
|
||||
|
||||
def test_prune_check_history(self):
|
||||
from .tasks import prune_check_history
|
||||
|
||||
# setup data
|
||||
check = baker.make_recipe("checks.diskspace_check")
|
||||
baker.make("checks.CheckHistory", check_history=check, _quantity=30)
|
||||
baker.make("checks.CheckHistory", check_id=check.id, _quantity=30)
|
||||
check_history_data = baker.make(
|
||||
"checks.CheckHistory",
|
||||
check_history=check,
|
||||
check_id=check.id,
|
||||
_quantity=30,
|
||||
)
|
||||
|
||||
@@ -526,6 +526,7 @@ class TestCheckTasks(TacticalTestCase):
|
||||
"percent_used": 85,
|
||||
"total": 500,
|
||||
"free": 400,
|
||||
"more_info": "More info",
|
||||
}
|
||||
|
||||
resp = self.client.patch(url, data, format="json")
|
||||
@@ -543,6 +544,7 @@ class TestCheckTasks(TacticalTestCase):
|
||||
"percent_used": 95,
|
||||
"total": 500,
|
||||
"free": 400,
|
||||
"more_info": "More info",
|
||||
}
|
||||
|
||||
resp = self.client.patch(url, data, format="json")
|
||||
@@ -573,6 +575,7 @@ class TestCheckTasks(TacticalTestCase):
|
||||
"percent_used": 95,
|
||||
"total": 500,
|
||||
"free": 400,
|
||||
"more_info": "More info",
|
||||
}
|
||||
|
||||
resp = self.client.patch(url, data, format="json")
|
||||
@@ -592,6 +595,7 @@ class TestCheckTasks(TacticalTestCase):
|
||||
"percent_used": 95,
|
||||
"total": 500,
|
||||
"free": 400,
|
||||
"more_info": "More info",
|
||||
}
|
||||
|
||||
resp = self.client.patch(url, data, format="json")
|
||||
@@ -608,6 +612,7 @@ class TestCheckTasks(TacticalTestCase):
|
||||
"percent_used": 50,
|
||||
"total": 500,
|
||||
"free": 400,
|
||||
"more_info": "More info",
|
||||
}
|
||||
|
||||
resp = self.client.patch(url, data, format="json")
|
||||
@@ -791,12 +796,7 @@ class TestCheckTasks(TacticalTestCase):
|
||||
)
|
||||
|
||||
# test failing info
|
||||
data = {
|
||||
"id": ping.id,
|
||||
"output": "Reply from 192.168.1.27: Destination host unreachable",
|
||||
"has_stdout": True,
|
||||
"has_stderr": False,
|
||||
}
|
||||
data = {"id": ping.id, "status": "failing", "output": "reply from a.com"}
|
||||
|
||||
resp = self.client.patch(url, data, format="json")
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
@@ -806,13 +806,6 @@ class TestCheckTasks(TacticalTestCase):
|
||||
self.assertEqual(new_check.alert_severity, "info")
|
||||
|
||||
# test failing warning
|
||||
data = {
|
||||
"id": ping.id,
|
||||
"output": "Reply from 192.168.1.27: Destination host unreachable",
|
||||
"has_stdout": True,
|
||||
"has_stderr": False,
|
||||
}
|
||||
|
||||
ping.alert_severity = "warning"
|
||||
ping.save()
|
||||
|
||||
@@ -824,13 +817,6 @@ class TestCheckTasks(TacticalTestCase):
|
||||
self.assertEqual(new_check.alert_severity, "warning")
|
||||
|
||||
# test failing error
|
||||
data = {
|
||||
"id": ping.id,
|
||||
"output": "Reply from 192.168.1.27: Destination host unreachable",
|
||||
"has_stdout": True,
|
||||
"has_stderr": False,
|
||||
}
|
||||
|
||||
ping.alert_severity = "error"
|
||||
ping.save()
|
||||
|
||||
@@ -842,13 +828,6 @@ class TestCheckTasks(TacticalTestCase):
|
||||
self.assertEqual(new_check.alert_severity, "error")
|
||||
|
||||
# test failing error
|
||||
data = {
|
||||
"id": ping.id,
|
||||
"output": "some output",
|
||||
"has_stdout": False,
|
||||
"has_stderr": True,
|
||||
}
|
||||
|
||||
resp = self.client.patch(url, data, format="json")
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
@@ -857,12 +836,7 @@ class TestCheckTasks(TacticalTestCase):
|
||||
self.assertEqual(new_check.alert_severity, "error")
|
||||
|
||||
# test passing
|
||||
data = {
|
||||
"id": ping.id,
|
||||
"output": "Reply from 192.168.1.1: bytes=32 time<1ms TTL=64",
|
||||
"has_stdout": True,
|
||||
"has_stderr": False,
|
||||
}
|
||||
data = {"id": ping.id, "status": "passing", "output": "reply from a.com"}
|
||||
|
||||
resp = self.client.patch(url, data, format="json")
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
@@ -881,7 +855,7 @@ class TestCheckTasks(TacticalTestCase):
|
||||
)
|
||||
|
||||
# test passing running
|
||||
data = {"id": winsvc.id, "exists": True, "status": "running"}
|
||||
data = {"id": winsvc.id, "status": "passing", "more_info": "ok"}
|
||||
|
||||
resp = self.client.patch(url, data, format="json")
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
@@ -889,20 +863,8 @@ class TestCheckTasks(TacticalTestCase):
|
||||
new_check = Check.objects.get(pk=winsvc.id)
|
||||
self.assertEqual(new_check.status, "passing")
|
||||
|
||||
# test passing start pending
|
||||
winsvc.pass_if_start_pending = True
|
||||
winsvc.save()
|
||||
|
||||
data = {"id": winsvc.id, "exists": True, "status": "start_pending"}
|
||||
|
||||
resp = self.client.patch(url, data, format="json")
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
new_check = Check.objects.get(pk=winsvc.id)
|
||||
self.assertEqual(new_check.status, "passing")
|
||||
|
||||
# test failing no start
|
||||
data = {"id": winsvc.id, "exists": True, "status": "not running"}
|
||||
# test failing
|
||||
data = {"id": winsvc.id, "status": "failing", "more_info": "ok"}
|
||||
|
||||
resp = self.client.patch(url, data, format="json")
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
@@ -911,7 +873,7 @@ class TestCheckTasks(TacticalTestCase):
|
||||
self.assertEqual(new_check.status, "failing")
|
||||
self.assertEqual(new_check.alert_severity, "info")
|
||||
|
||||
# test failing and attempt start
|
||||
""" # test failing and attempt start
|
||||
winsvc.restart_if_stopped = True
|
||||
winsvc.alert_severity = "warning"
|
||||
winsvc.save()
|
||||
@@ -976,9 +938,9 @@ class TestCheckTasks(TacticalTestCase):
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
new_check = Check.objects.get(pk=winsvc.id)
|
||||
self.assertEqual(new_check.status, "passing")
|
||||
self.assertEqual(new_check.status, "passing") """
|
||||
|
||||
def test_handle_eventlog_check(self):
|
||||
""" def test_handle_eventlog_check(self):
|
||||
from checks.models import Check
|
||||
|
||||
url = "/api/v3/checkrunner/"
|
||||
@@ -1180,4 +1142,4 @@ class TestCheckTasks(TacticalTestCase):
|
||||
|
||||
new_check = Check.objects.get(pk=eventlog.id)
|
||||
|
||||
self.assertEquals(new_check.status, "passing")
|
||||
self.assertEquals(new_check.status, "passing") """
|
||||
|
||||
@@ -8,5 +8,5 @@ urlpatterns = [
|
||||
path("<pk>/loadchecks/", views.load_checks),
|
||||
path("getalldisks/", views.get_disks_for_policies),
|
||||
path("runchecks/<pk>/", views.run_checks),
|
||||
path("history/<int:checkpk>/", views.CheckHistory.as_view()),
|
||||
path("history/<int:checkpk>/", views.GetCheckHistory.as_view()),
|
||||
]
|
||||
|
||||
@@ -15,7 +15,7 @@ from automation.models import Policy
|
||||
from scripts.models import Script
|
||||
from tacticalrmm.utils import notify_error
|
||||
|
||||
from .models import Check
|
||||
from .models import Check, CheckHistory
|
||||
from .permissions import ManageChecksPerms, RunChecksPerms
|
||||
from .serializers import CheckHistorySerializer, CheckSerializer
|
||||
|
||||
@@ -146,7 +146,7 @@ class GetUpdateDeleteCheck(APIView):
|
||||
return Response(f"{check.readable_desc} was deleted!")
|
||||
|
||||
|
||||
class CheckHistory(APIView):
|
||||
class GetCheckHistory(APIView):
|
||||
def patch(self, request, checkpk):
|
||||
check = get_object_or_404(Check, pk=checkpk)
|
||||
|
||||
@@ -160,7 +160,7 @@ class CheckHistory(APIView):
|
||||
- djangotime.timedelta(days=request.data["timeFilter"]),
|
||||
)
|
||||
|
||||
check_history = check.check_history.filter(timeFilter).order_by("-x") # type: ignore
|
||||
check_history = CheckHistory.objects.filter(check_id=checkpk).filter(timeFilter).order_by("-x") # type: ignore
|
||||
|
||||
return Response(
|
||||
CheckHistorySerializer(
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from agents.models import Agent
|
||||
from logs.models import PendingAction
|
||||
from scripts.models import Script
|
||||
|
||||
@@ -9,22 +8,6 @@ class Command(BaseCommand):
|
||||
help = "Collection of tasks to run after updating the rmm, after migrations"
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
# 10-16-2020 changed the type of the agent's 'disks' model field
|
||||
# from a dict of dicts, to a list of disks in the golang agent
|
||||
# the following will convert dicts to lists for agent's still on the python agent
|
||||
agents = Agent.objects.only("pk", "disks")
|
||||
for agent in agents:
|
||||
if agent.disks is not None and isinstance(agent.disks, dict):
|
||||
new = []
|
||||
for k, v in agent.disks.items():
|
||||
new.append(v)
|
||||
|
||||
agent.disks = new
|
||||
agent.save(update_fields=["disks"])
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(f"Migrated disks on {agent.hostname}")
|
||||
)
|
||||
|
||||
# remove task pending actions. deprecated 4/20/2021
|
||||
PendingAction.objects.filter(action_type="taskaction").delete()
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ def dashboard_info(request):
|
||||
"client_tree_sort": request.user.client_tree_sort,
|
||||
"client_tree_splitter": request.user.client_tree_splitter,
|
||||
"loading_bar_color": request.user.loading_bar_color,
|
||||
"no_code_sign": hasattr(settings, "NOCODESIGN") and settings.NOCODESIGN,
|
||||
"hosted": hasattr(settings, "HOSTED") and settings.HOSTED,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
asgiref==3.3.4
|
||||
asyncio-nats-client==0.11.4
|
||||
celery==5.0.5
|
||||
certifi==2020.12.5
|
||||
celery==5.1.0
|
||||
certifi==2021.5.30
|
||||
cffi==1.14.5
|
||||
channels==3.0.3
|
||||
channels_redis==3.2.0
|
||||
chardet==4.0.0
|
||||
cryptography==3.4.7
|
||||
daphne==3.0.2
|
||||
Django==3.2.2
|
||||
Django==3.2.4
|
||||
django-cors-headers==3.7.0
|
||||
django-rest-knox==4.1.0
|
||||
djangorestframework==3.12.4
|
||||
future==0.18.2
|
||||
kombu==5.0.2
|
||||
loguru==0.5.3
|
||||
msgpack==1.0.2
|
||||
packaging==20.9
|
||||
@@ -26,10 +25,10 @@ pytz==2021.1
|
||||
qrcode==6.1
|
||||
redis==3.5.3
|
||||
requests==2.25.1
|
||||
six==1.15.0
|
||||
six==1.16.0
|
||||
sqlparse==0.4.1
|
||||
twilio==6.57.0
|
||||
urllib3==1.26.4
|
||||
twilio==6.59.1
|
||||
urllib3==1.26.5
|
||||
uWSGI==2.0.19.1
|
||||
validators==0.18.2
|
||||
vine==5.0.0
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
"name": "Firefox - Clean Cache",
|
||||
"description": "This script will clean up Mozilla Firefox for all users.",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Browsers"
|
||||
"category": "TRMM (Win):Browsers",
|
||||
"default_timeout": "300"
|
||||
|
||||
},
|
||||
{
|
||||
"guid": "3ff6a386-11d1-4f9d-8cca-1b0563bb6443",
|
||||
@@ -15,7 +17,8 @@
|
||||
"name": "Chrome - Clear Cache for All Users",
|
||||
"description": "This script will clean up Google Chrome for all users.",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Browsers"
|
||||
"category": "TRMM (Win):Browsers",
|
||||
"default_timeout": "300"
|
||||
},
|
||||
{
|
||||
"guid": "be1de837-f677-4ac5-aa0c-37a0fc9991fc",
|
||||
@@ -24,7 +27,8 @@
|
||||
"name": "Adobe Reader DC - Install",
|
||||
"description": "Installs Adobe Reader DC.",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):3rd Party Software>Chocolatey"
|
||||
"category": "TRMM (Win):3rd Party Software>Chocolatey",
|
||||
"default_timeout": "300"
|
||||
},
|
||||
{
|
||||
"guid": "2ee134d5-76aa-4160-b334-a1efbc62079f",
|
||||
@@ -33,7 +37,8 @@
|
||||
"name": "Duplicati - Install",
|
||||
"description": "This script installs Duplicati 2.0.5.1 as a service.",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):3rd Party Software"
|
||||
"category": "TRMM (Win):3rd Party Software",
|
||||
"default_timeout": "300"
|
||||
},
|
||||
{
|
||||
"guid": "81cc5bcb-01bf-4b0c-89b9-0ac0f3fe0c04",
|
||||
@@ -42,7 +47,8 @@
|
||||
"name": "Windows Update - Reset",
|
||||
"description": "This script will reset all of the Windows Updates components to DEFAULT SETTINGS.",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Updates"
|
||||
"category": "TRMM (Win):Updates",
|
||||
"default_timeout": "300"
|
||||
},
|
||||
{
|
||||
"guid": "8db87ff0-a9b4-4d9d-bc55-377bbcb85b6d",
|
||||
@@ -51,7 +57,8 @@
|
||||
"name": "Disk - Cleanup C: drive",
|
||||
"description": "Cleans the C: drive's Window Temperary files, Windows SoftwareDistribution folder, the local users Temperary folder, IIS logs (if applicable) and empties the recycling bin. All deleted files will go into a log transcript in $env:TEMP. By default this script leaves files that are newer than 7 days old however this variable can be edited.",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Maintenance"
|
||||
"category": "TRMM (Win):Maintenance",
|
||||
"default_timeout": "25000"
|
||||
},
|
||||
{
|
||||
"guid": "2f28e8c1-ae0f-4b46-a826-f513974526a3",
|
||||
@@ -78,7 +85,8 @@
|
||||
"name": "Speed Test - Python",
|
||||
"description": "Runs a Speed Test using Python",
|
||||
"shell": "python",
|
||||
"category": "TRMM (Win):Network"
|
||||
"category": "TRMM (Win):Network",
|
||||
"default_timeout": "120"
|
||||
},
|
||||
{
|
||||
"guid": "9d34f482-1f0c-4b2f-b65f-a9cf3c13ef5f",
|
||||
@@ -161,6 +169,18 @@
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Collectors"
|
||||
},
|
||||
{
|
||||
"guid": "973c34d7-cab0-4fda-999c-b4933655f946",
|
||||
"filename": "Win_Screenconnect_GetGUID.ps1",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "Screenconnect - Get GUID for client",
|
||||
"description": "Returns Screenconnect GUID for client - Use with Custom Fields for later use. ",
|
||||
"args": [
|
||||
"-serviceName {{client.ScreenConnectService}}"
|
||||
],
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Collectors"
|
||||
},
|
||||
{
|
||||
"guid": "95a2ee6f-b89b-4551-856e-3081b041caa7",
|
||||
"filename": "Win_Power_Profile_Reset_High_Performance_to_Defaults.ps1",
|
||||
@@ -186,7 +206,8 @@
|
||||
"name": "Windows 10 Upgrade",
|
||||
"description": "Forces an upgrade to the latest release of Windows 10.",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Updates"
|
||||
"category": "TRMM (Win):Updates",
|
||||
"default_timeout": "25000"
|
||||
},
|
||||
{
|
||||
"guid": "375323e5-cac6-4f35-a304-bb7cef35902d",
|
||||
@@ -222,7 +243,8 @@
|
||||
"name": "SSH - Install Feature and Enable",
|
||||
"description": "Installs and enabled OpenSSH Server Feature in Win10",
|
||||
"shell": "powershell",
|
||||
"category": "TRMM (Win):Windows Features"
|
||||
"category": "TRMM (Win):Windows Features",
|
||||
"default_timeout": "300"
|
||||
},
|
||||
{
|
||||
"guid": "2435297a-6263-4e90-8688-1847400d0e22",
|
||||
@@ -339,7 +361,8 @@
|
||||
"name": "Update Installed Apps",
|
||||
"description": "Update all apps that were installed using Chocolatey.",
|
||||
"shell": "cmd",
|
||||
"category": "TRMM (Win):3rd Party Software>Chocolatey"
|
||||
"category": "TRMM (Win):3rd Party Software>Chocolatey",
|
||||
"default_timeout": "3600"
|
||||
},
|
||||
{
|
||||
"guid": "fff8024d-d72e-4457-84fa-6c780f69a16f",
|
||||
@@ -660,4 +683,4 @@
|
||||
"category": "TRMM (Win):Misc>Reference",
|
||||
"default_timeout": "1"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -41,7 +41,7 @@ app.conf.beat_schedule = {
|
||||
},
|
||||
"get-wmi": {
|
||||
"task": "agents.tasks.get_wmi_task",
|
||||
"schedule": crontab(minute="*/18"),
|
||||
"schedule": crontab(minute=18, hour="*/5"),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -15,22 +15,22 @@ EXE_DIR = os.path.join(BASE_DIR, "tacticalrmm/private/exe")
|
||||
AUTH_USER_MODEL = "accounts.User"
|
||||
|
||||
# latest release
|
||||
TRMM_VERSION = "0.6.11"
|
||||
TRMM_VERSION = "0.6.14"
|
||||
|
||||
# bump this version everytime vue code is changed
|
||||
# to alert user they need to manually refresh their browser
|
||||
APP_VER = "0.0.136"
|
||||
APP_VER = "0.0.138"
|
||||
|
||||
# https://github.com/wh1te909/rmmagent
|
||||
LATEST_AGENT_VER = "1.5.6"
|
||||
LATEST_AGENT_VER = "1.5.8"
|
||||
|
||||
MESH_VER = "0.8.35"
|
||||
MESH_VER = "0.8.60"
|
||||
|
||||
# for the update script, bump when need to recreate venv or npm install
|
||||
PIP_VER = "16"
|
||||
NPM_VER = "15"
|
||||
PIP_VER = "18"
|
||||
NPM_VER = "17"
|
||||
|
||||
SETUPTOOLS_VER = "56.1.0"
|
||||
SETUPTOOLS_VER = "57.0.0"
|
||||
WHEEL_VER = "0.36.2"
|
||||
|
||||
DL_64 = f"https://github.com/wh1te909/rmmagent/releases/download/v{LATEST_AGENT_VER}/winagent-v{LATEST_AGENT_VER}.exe"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
SCRIPT_VERSION="12"
|
||||
SCRIPT_VERSION="13"
|
||||
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/backup.sh'
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
@@ -59,6 +59,7 @@ mkdir ${tmp_dir}/nginx
|
||||
mkdir ${tmp_dir}/systemd
|
||||
mkdir ${tmp_dir}/rmm
|
||||
mkdir ${tmp_dir}/confd
|
||||
mkdir ${tmp_dir}/redis
|
||||
|
||||
|
||||
pg_dump --dbname=postgresql://"${POSTGRES_USER}":"${POSTGRES_PW}"@127.0.0.1:5432/tacticalrmm | gzip -9 > ${tmp_dir}/postgres/db-${dt_now}.psql.gz
|
||||
@@ -72,6 +73,8 @@ sudo tar -czvf ${tmp_dir}/nginx/etc-nginx.tar.gz -C /etc/nginx .
|
||||
|
||||
sudo tar -czvf ${tmp_dir}/confd/etc-confd.tar.gz -C /etc/conf.d .
|
||||
|
||||
sudo tar -czvf ${tmp_dir}/redis/etc-redis.tar.gz -C /var/lib/redis/appendonly.aof
|
||||
|
||||
sudo cp ${sysd}/rmm.service ${sysd}/celery.service ${sysd}/celerybeat.service ${sysd}/meshcentral.service ${sysd}/nats.service ${tmp_dir}/systemd/
|
||||
if [ -f "${sysd}/daphne.service" ]; then
|
||||
sudo cp ${sysd}/daphne.service ${tmp_dir}/systemd/
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM nats:2.2-alpine
|
||||
FROM nats:2.2.6-alpine
|
||||
|
||||
ENV TACTICAL_DIR /opt/tactical
|
||||
ENV TACTICAL_READY_FILE ${TACTICAL_DIR}/tmp/tactical.ready
|
||||
|
||||
@@ -18,6 +18,7 @@ volumes:
|
||||
postgres_data:
|
||||
mongo_data:
|
||||
mesh_data:
|
||||
redis_data:
|
||||
|
||||
services:
|
||||
# postgres database for api service
|
||||
@@ -38,7 +39,10 @@ services:
|
||||
tactical-redis:
|
||||
container_name: trmm-redis
|
||||
image: redis:6.0-alpine
|
||||
command: redis-server --appendonly yes
|
||||
restart: always
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
networks:
|
||||
- redis
|
||||
|
||||
|
||||
@@ -61,6 +61,8 @@ Category or Function - What It Does
|
||||
|
||||

|
||||
|
||||
*****
|
||||
|
||||
## Making Script Files
|
||||
|
||||
### Good Habits
|
||||
@@ -117,6 +119,8 @@ c:\ProgramData\TacticalRMM\
|
||||
- Doesn't play well with other community scripts (reused names etc.)
|
||||
|
||||
|
||||
*****
|
||||
|
||||
## Useful Reference Script Examples
|
||||
|
||||
RunAsUser (since Tactical RMM runs as system)
|
||||
@@ -128,6 +132,8 @@ Command Paramater Ninja
|
||||
Optional Command Parameters and testing for errors
|
||||
[https://github.com/wh1te909/tacticalrmm/blob/develop/scripts/Win_Rename_Computer.ps1](https://github.com/wh1te909/tacticalrmm/blob/develop/scripts/Win_Rename_Computer.ps1)
|
||||
|
||||
*****
|
||||
|
||||
## Volunteers Needed
|
||||
|
||||
If you want to contribute back to the project there are a lot of scripts that need some TLC (Tender Loving Care) please paruse thru them here: [https://github.com/wh1te909/tacticalrmm/tree/develop/scripts_wip](https://github.com/wh1te909/tacticalrmm/tree/develop/scripts_wip)
|
||||
|
||||
@@ -105,6 +105,7 @@ Then you're `push`ing that updated local repo to your online Github fork
|
||||
|
||||
Check your Github fork in browser, should be up to date now with original. Repeat 6 or 7 as necessary
|
||||
|
||||
*****
|
||||
## Reference
|
||||
|
||||
### Customizing the Admin Web Interface
|
||||
|
||||
31
docs/docs/functions/examples.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Examples
|
||||
|
||||
## Create Run URL Action to Computer support page
|
||||
|
||||
This will create a URL link that will take you to the support page for a computer based on the computers Serial Number
|
||||
|
||||
1. Goto `Settings | Global Settings | Custom Fields`
|
||||
|
||||
Under Agents tab Add Custom Field (CaSe SeNsItIve)
|
||||
|
||||

|
||||
|
||||
2. Create Task (best to use `Settings | Automation Manager` if you want to apply it to all computers). Add script that has an output of the data you want.
|
||||
|
||||

|
||||
|
||||
3. Create URL Action (under `Settings | Global Settings | URL ACTIONS`) for Manufacturer websites
|
||||
|
||||

|
||||
|
||||
Dell Support Page
|
||||
|
||||
```
|
||||
https://www.dell.com/support/home/en-us/product-support/servicetag/{{agent.SerialNumber}}/overview
|
||||
```
|
||||
|
||||
Lenovo Support Page
|
||||
|
||||
```
|
||||
https://www.dell.com/support/home/en-us/product-support/servicetag/{{agent.SerialNumber}}/overview
|
||||
```
|
||||
58
docs/docs/howitallworks.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# How It All Works
|
||||
|
||||
INSERT WIREFRAME GRAPHIC HERE USING <https://www.yworks.com/yed-live/>
|
||||
|
||||
## Server
|
||||
|
||||
## Windows Agent
|
||||
|
||||
Found in `%programfiles%\TacticalAgent`
|
||||
|
||||
### Services
|
||||
|
||||
3 services exist on all clients
|
||||
|
||||
* `Mesh Agent`
|
||||

|
||||

|
||||
|
||||
**AND**
|
||||
|
||||
* `TacticalAgent` and `Tactical RMM RPC Service`
|
||||

|
||||

|
||||
|
||||
The [MeshCentral](https://meshcentral.com/) system which is accessible from <https://mesh.example.com> and is used
|
||||
|
||||
`Tactical RMM Agent`
|
||||
|
||||
* It runs 2 goroutines
|
||||
* one is the checkrunner which runs all the checks and then just sleeps until it's time to run more checks
|
||||
* 2nd goroutine periodically sends info about the agent to the rmm and also handles agent recovery
|
||||
|
||||
!!!note
|
||||
In Task Manager you will see additional `Tactical RMM Agent` processes appear and disappear. These are your Checks and Tasks running at scheduled intervals
|
||||
|
||||
`Tactical RMM RPC Service`
|
||||
|
||||
* Uses the pub/sub model so anytime you do anything realtime from rmm (like a send command or run script)
|
||||
* It maintains a persistent connection to your to the api.example.com rmm server on port **443 CONFIRM** and is listening for events
|
||||
* It handles your Agent updates (Auto triggers at 35mins past every hour or when run manually from server Agents | Update Agents menu)
|
||||
|
||||
***
|
||||
|
||||
### Agent Installation Process
|
||||
|
||||
Adds Defender AV exclusions
|
||||
|
||||
Copies
|
||||
|
||||
***
|
||||
|
||||
### Agent Update Process
|
||||
|
||||
Downloads latest `winagent-vx.x.x-x86/64.exe` to `%programfiles%`
|
||||
|
||||
Executes the file (INNO setup exe)
|
||||
|
||||
Files create `c:\Windows\temp\Tacticalxxxx\` folder for install (and log files)
|
||||
BIN
docs/docs/images/example1_customfield.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
docs/docs/images/example1_taskcollectorscript.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
docs/docs/images/example1_urlaction.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
docs/docs/images/trmm_services.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
docs/docs/images/trmm_services__taskmanager_agent.png
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
docs/docs/images/trmm_services__taskmanager_mesh.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
docs/docs/images/trmm_services_mesh.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
docs/docs/images/trmm_user_preferences.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
@@ -14,6 +14,10 @@
|
||||
|
||||
## Install
|
||||
|
||||
!!!info
|
||||
It is recommended that you keep your server updated regularly (monthly). SSL wildcard certs will expire every 3 months and need manual updating as well. <br/><br/>
|
||||
Until we reach production release, there may be architectural changes that may be made to Tactical RMM and only a regular patching schedule is supported by developers.
|
||||
|
||||
#### Run updates and setup the linux user
|
||||
SSH into the server as **root**.<br/><br/>
|
||||
Download and run the prereqs and latest updates<br/>
|
||||
|
||||
52
docs/docs/tipsntricks.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Tips and Tricks
|
||||
|
||||
## Customize User Interface
|
||||
|
||||
Top right click Username, look at preferences pane. Set default tab: Servers|Workstations|Mixed
|
||||
|
||||

|
||||
|
||||
*****
|
||||
|
||||
## Screenconnect / Connectwise Control
|
||||
|
||||
### Install Tactical RMM via Screeconnect commands window
|
||||
|
||||
1. Create a Deplopment under Agents | Manage Deployments
|
||||
2. Replace `<deployment URL>` below with your Deployment Download Link.
|
||||
|
||||
**x64**
|
||||
|
||||
```cmd
|
||||
#!ps
|
||||
#maxlength=500000
|
||||
#timeout=600000
|
||||
|
||||
Invoke-WebRequest "<deployment URL>" -OutFile ( New-Item -Path "C:\temp\trmminstallx64.exe" -Force )
|
||||
$proc = Start-Process c:\temp\trmminstallx64.exe -ArgumentList '-silent' -PassThru
|
||||
Wait-Process -InputObject $proc
|
||||
|
||||
if ($proc.ExitCode -ne 0) {
|
||||
Write-Warning "$_ exited with status code $($proc.ExitCode)"
|
||||
}
|
||||
Remove-Item -Path "c:\temp\trmminstallx64.exe" -Force
|
||||
```
|
||||
|
||||
**x86**
|
||||
|
||||
```cmd
|
||||
#!ps
|
||||
#maxlength=500000
|
||||
#timeout=600000
|
||||
|
||||
Invoke-WebRequest "<deployment URL>" -OutFile ( New-Item -Path "C:\temp\trmminstallx86.exe" -Force )
|
||||
$proc = Start-Process c:\temp\trmminstallx86.exe -ArgumentList '-silent' -PassThru
|
||||
Wait-Process -InputObject $proc
|
||||
|
||||
if ($proc.ExitCode -ne 0) {
|
||||
Write-Warning "$_ exited with status code $($proc.ExitCode)"
|
||||
}
|
||||
Remove-Item -Path "c:\temp\trmminstallx86.exe" -Force
|
||||
```
|
||||
|
||||
###
|
||||
@@ -23,6 +23,7 @@ nav:
|
||||
- "User Interface Preferences": functions/user_ui.md
|
||||
- "Django Admin": functions/django_admin.md
|
||||
- "Settings Override": functions/settings_override.md
|
||||
- "Examples": functions/examples.md
|
||||
- Backup: backup.md
|
||||
- Restore: restore.md
|
||||
- Troubleshooting: troubleshooting.md
|
||||
|
||||
2
go.mod
@@ -5,6 +5,6 @@ go 1.16
|
||||
require (
|
||||
github.com/nats-io/nats-server/v2 v2.1.8-0.20201129161730-ebe63db3e3ed // indirect
|
||||
github.com/nats-io/nats.go v1.11.0
|
||||
github.com/ugorji/go/codec v1.2.5
|
||||
github.com/ugorji/go/codec v1.2.6
|
||||
golang.org/x/sys v0.0.0-20210122235752-a8b976e07c7b // indirect
|
||||
)
|
||||
|
||||
8
go.sum
@@ -34,10 +34,10 @@ github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8=
|
||||
github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/ugorji/go v1.2.5 h1:NozRHfUeEta89taVkyfsDVSy2f7v89Frft4pjnWuGuc=
|
||||
github.com/ugorji/go v1.2.5/go.mod h1:gat2tIT8KJG8TVI8yv77nEO/KYT6dV7JE1gfUa8Xuls=
|
||||
github.com/ugorji/go/codec v1.2.5 h1:8WobZKAk18Msm2CothY2jnztY56YVY8kF1oQrj21iis=
|
||||
github.com/ugorji/go/codec v1.2.5/go.mod h1:QPxoTbPKSEAlAHPYt02++xp/en9B/wUdwFCz+hj5caA=
|
||||
github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E=
|
||||
github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=
|
||||
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
|
||||
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
|
||||
20
install.sh
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
SCRIPT_VERSION="48"
|
||||
SCRIPT_VERSION="50"
|
||||
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/install.sh'
|
||||
|
||||
sudo apt install -y curl wget dirmngr gnupg lsb-release
|
||||
@@ -167,11 +167,11 @@ sudo chmod 775 -R /etc/letsencrypt
|
||||
print_green 'Downloading NATS'
|
||||
|
||||
nats_tmp=$(mktemp -d -t nats-XXXXXXXXXX)
|
||||
wget https://github.com/nats-io/nats-server/releases/download/v2.2.3/nats-server-v2.2.3-linux-amd64.tar.gz -P ${nats_tmp}
|
||||
wget https://github.com/nats-io/nats-server/releases/download/v2.2.6/nats-server-v2.2.6-linux-amd64.tar.gz -P ${nats_tmp}
|
||||
|
||||
tar -xzf ${nats_tmp}/nats-server-v2.2.3-linux-amd64.tar.gz -C ${nats_tmp}
|
||||
tar -xzf ${nats_tmp}/nats-server-v2.2.6-linux-amd64.tar.gz -C ${nats_tmp}
|
||||
|
||||
sudo mv ${nats_tmp}/nats-server-v2.2.3-linux-amd64/nats-server /usr/local/bin/
|
||||
sudo mv ${nats_tmp}/nats-server-v2.2.6-linux-amd64/nats-server /usr/local/bin/
|
||||
sudo chmod +x /usr/local/bin/nats-server
|
||||
sudo chown ${USER}:${USER} /usr/local/bin/nats-server
|
||||
rm -rf ${nats_tmp}
|
||||
@@ -217,6 +217,10 @@ sudo rm -rf Python-3.9.2 Python-3.9.2.tgz
|
||||
print_green 'Installing redis and git'
|
||||
sudo apt install -y ca-certificates redis git
|
||||
|
||||
# apply redis configuration
|
||||
sudo redis-cli config set appendonly yes
|
||||
sudo redis-cli config rewrite
|
||||
|
||||
print_green 'Installing postgresql'
|
||||
|
||||
echo "$postgresql_repo" | sudo tee /etc/apt/sources.list.d/pgdg.list
|
||||
@@ -487,12 +491,14 @@ map \$http_user_agent \$ignore_ua {
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name ${rmmdomain};
|
||||
return 301 https://\$server_name\$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name ${rmmdomain};
|
||||
client_max_body_size 300M;
|
||||
access_log /rmm/api/tacticalrmm/tacticalrmm/private/log/access.log combined if=\$ignore_ua;
|
||||
@@ -549,6 +555,7 @@ echo "${nginxrmm}" | sudo tee /etc/nginx/sites-available/rmm.conf > /dev/null
|
||||
nginxmesh="$(cat << EOF
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name ${meshdomain};
|
||||
return 301 https://\$server_name\$request_uri;
|
||||
}
|
||||
@@ -556,6 +563,7 @@ server {
|
||||
server {
|
||||
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
proxy_send_timeout 330s;
|
||||
proxy_read_timeout 330s;
|
||||
server_name ${meshdomain};
|
||||
@@ -710,6 +718,7 @@ server {
|
||||
access_log /var/log/nginx/frontend-access.log;
|
||||
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
ssl_certificate ${CERT_PUB_KEY};
|
||||
ssl_certificate_key ${CERT_PRIV_KEY};
|
||||
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
|
||||
@@ -720,7 +729,8 @@ server {
|
||||
return 301 https://\$host\$request_uri;
|
||||
}
|
||||
|
||||
listen 80;
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name ${frontenddomain};
|
||||
return 404;
|
||||
}
|
||||
|
||||
15
restore.sh
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
SCRIPT_VERSION="26"
|
||||
SCRIPT_VERSION="28"
|
||||
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/restore.sh'
|
||||
|
||||
sudo apt update
|
||||
@@ -108,11 +108,11 @@ sudo apt update
|
||||
print_green 'Downloading NATS'
|
||||
|
||||
nats_tmp=$(mktemp -d -t nats-XXXXXXXXXX)
|
||||
wget https://github.com/nats-io/nats-server/releases/download/v2.2.3/nats-server-v2.2.3-linux-amd64.tar.gz -P ${nats_tmp}
|
||||
wget https://github.com/nats-io/nats-server/releases/download/v2.2.6/nats-server-v2.2.6-linux-amd64.tar.gz -P ${nats_tmp}
|
||||
|
||||
tar -xzf ${nats_tmp}/nats-server-v2.2.3-linux-amd64.tar.gz -C ${nats_tmp}
|
||||
tar -xzf ${nats_tmp}/nats-server-v2.2.6-linux-amd64.tar.gz -C ${nats_tmp}
|
||||
|
||||
sudo mv ${nats_tmp}/nats-server-v2.2.3-linux-amd64/nats-server /usr/local/bin/
|
||||
sudo mv ${nats_tmp}/nats-server-v2.2.6-linux-amd64/nats-server /usr/local/bin/
|
||||
sudo chmod +x /usr/local/bin/nats-server
|
||||
sudo chown ${USER}:${USER} /usr/local/bin/nats-server
|
||||
rm -rf ${nats_tmp}
|
||||
@@ -189,6 +189,13 @@ sudo rm -rf Python-3.9.2 Python-3.9.2.tgz
|
||||
print_green 'Installing redis and git'
|
||||
sudo apt install -y ca-certificates redis git
|
||||
|
||||
# redis configuration
|
||||
sudo tar -xzf ${tmp_dir}/redis/etc-redis.tar.gz -C /var/lib/redis
|
||||
sudo redis-check-aof --fix /var/lib/redis/appendonly.aof
|
||||
|
||||
sudo redis-cli config set appendonly yes
|
||||
sudo redis-cli config rewrite
|
||||
|
||||
print_green 'Installing postgresql'
|
||||
|
||||
echo "$postgresql_repo" | sudo tee /etc/apt/sources.list.d/pgdg.list
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#antivirusName must match the "displayName" exactly
|
||||
#If no antivirusName parameter is specified, the tool returns success if there is any active up to date antivirus on the system
|
||||
|
||||
# OS Build must be greater than 14393 to support this script. If it's not it returns exit code 2
|
||||
|
||||
|
||||
param($antivirusName = "*")
|
||||
|
||||
@@ -93,6 +95,11 @@ function Add-ProductStates {
|
||||
}
|
||||
}
|
||||
|
||||
if ([environment]::OSVersion.Version.Build -le 14393) {
|
||||
write-host "Antivirus check not supported on this OS. Returning Exit Code 2."
|
||||
exit 2
|
||||
}
|
||||
|
||||
|
||||
$return = Get-CimInstance -Namespace root/SecurityCenter2 -className AntivirusProduct |
|
||||
Where-Object {
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
## Copied from https://github.com/ThatsNASt/tacticalrmm to add to new pull request for https://github.com/wh1te909/tacticalrmm
|
||||
#
|
||||
# WARNING
|
||||
# 1. Only applies to drive C
|
||||
# 2. Assumes you're encrypting more than the used space. "Used Space Only Encrypted" is the default windows behavior which is not compatible here.
|
||||
|
||||
function Log-Message {
|
||||
Param
|
||||
(
|
||||
@@ -22,7 +27,7 @@ function Log-Message {
|
||||
$log = "BitlockerReport.txt"
|
||||
|
||||
#Find BL info
|
||||
$mbde = [string](manage-bde -status)
|
||||
$mbde = [string](manage-bde -status C:)
|
||||
$mbdeProt = (manage-bde -protectors -get c: | Select-Object -Skip 6)
|
||||
#Dig out the recovery password, check for PIN
|
||||
ForEach ($line in $mbdeProt) {
|
||||
@@ -94,4 +99,4 @@ if ($Encrypted -eq "Yes" -and $RecoveryPassword -and $PIN -eq $true) {
|
||||
Log-Message "SUCCESS: Encrypted, PIN enabled, password is set." $log e
|
||||
Write-Host "Script check passed"
|
||||
exit 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
cup -y all
|
||||
cup all -y
|
||||
|
||||
@@ -31,6 +31,7 @@ else {
|
||||
Install-Module -Name RunAsUser -Force
|
||||
}
|
||||
|
||||
# Used to pull variables in and use them inside the script block. Contains message to show user
|
||||
Set-Content -Path c:\windows\temp\message.txt -Value $args
|
||||
|
||||
Invoke-AsCurrentUser -scriptblock {
|
||||
@@ -55,5 +56,5 @@ Invoke-AsCurrentUser -scriptblock {
|
||||
Submit-BTNotification -Content $Content
|
||||
}
|
||||
|
||||
|
||||
# Cleanup temp file for message variables
|
||||
Remove-Item -Path c:\windows\temp\message.txt
|
||||
|
||||
@@ -10,28 +10,6 @@
|
||||
Submitted by: https://github.com/dinger1986
|
||||
#>
|
||||
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
|
||||
$regpath = HKCU:\Software\Microsoft\Office\16.0\Outlook\Preferences
|
||||
$regname = DelegateSentItemsStyle
|
||||
$regvalue = 1
|
||||
$regproperty = Dword
|
||||
|
||||
|
||||
If (!(test-path '%ProgramData%\Tactical RMM\temp')) {
|
||||
New-Item -ItemType Directory -Force -Path '%ProgramData%\Tactical RMM\temp'
|
||||
}
|
||||
|
||||
If (!(test-path C:\TEMP\curpsxpolicy.txt)) {
|
||||
$curexpolicy = Get-ExecutionPolicy
|
||||
|
||||
(
|
||||
echo $curexpolicy
|
||||
)>"%ProgramData%\Tactical RMM\temp\curpsxpolicy.txt"
|
||||
}
|
||||
|
||||
Set-ExecutionPolicy -ExecutionPolicy Unrestricted
|
||||
|
||||
if (Get-PackageProvider -Name NuGet) {
|
||||
Write-Output "NuGet Already Added"
|
||||
}
|
||||
@@ -48,14 +26,30 @@ else {
|
||||
Install-Module -Name RunAsUser -Force
|
||||
}
|
||||
|
||||
If (!(test-path $env:programdata\TacticalRMM\temp\)) {
|
||||
New-Item -ItemType Directory -Force -Path $env:programdata\TacticalRMM\temp\
|
||||
}
|
||||
|
||||
If (!(test-path $env:programdata\TacticalRMM\temp\curpsxpolicy.txt)) {
|
||||
$curexpolicy = Get-ExecutionPolicy
|
||||
|
||||
(
|
||||
Write-Output $curexpolicy
|
||||
)>$env:programdata\TacticalRMM\temp\curpsxpolicy.txt
|
||||
}
|
||||
Set-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell -Name ExecutionPolicy -Value Unrestricted
|
||||
|
||||
Invoke-AsCurrentUser -scriptblock {
|
||||
New-ItemProperty -Path "$regpath" -Name "$regname" -Value "$regvalue" -PropertyType "$regproperty"
|
||||
# Modify below for other versions of Office
|
||||
$regpath = 'Software\Microsoft\Office\16.0\Outlook\Preferences'
|
||||
$regname = "DelegateSentItemsStyle"
|
||||
$regvalue = "1"
|
||||
$regproperty = "Dword"
|
||||
New-ItemProperty -Path HKCU:\$regpath -Name $regname -Value $regvalue -PropertyType $regproperty
|
||||
}
|
||||
|
||||
Write-Output "Successfully changed Sent Items for Delegated folders"
|
||||
|
||||
$curpsxpol = Get-Content -Path "%ProgramData%\Tactical RMM\temp\curpsxpolicy.txt";
|
||||
$curpsxpol = Get-Content -Path $env:programdata\TacticalRMM\temp\curpsxpolicy.txt;
|
||||
|
||||
Set-ExecutionPolicy -ExecutionPolicy $curpsxpol
|
||||
|
||||
del "%ProgramData%\Tactical RMM\temp\curpsxpolicy.txt"
|
||||
Set-ExecutionPolicy -ExecutionPolicy $curpsxpol
|
||||
26
scripts/Win_Screenconnect_GetGUID.ps1
Normal file
@@ -0,0 +1,26 @@
|
||||
<#
|
||||
Requires global variables for serviceName "ScreenConnectService"
|
||||
serviceName is the name of the ScreenConnect Service once it is installed EG: "ScreenConnect Client (1327465grctq84yrtocq)"
|
||||
Variable value must start and end with " (Prior to TRMM Version 0.6.5), remove / don't use " on TRMM Version 0.6.5 or later.
|
||||
Requires Custom Fields Agent entry Name: ScreenConnectGUID Type: text
|
||||
URL Action entry (check your screenconnect to see what folder name is your "All Machines" folder): https://YOURNAME.screenconnect.com/Host#Access/All%20Machines//{{agent.ScreenConnectGUID}}/Join
|
||||
or https://YOURNAME.screenconnect.com/Host#Access/All%20Machines%20by%20Company//{{agent.ScreenConnectGUID}}/Join
|
||||
#>
|
||||
|
||||
param (
|
||||
[string] $serviceName
|
||||
)
|
||||
|
||||
if (!$serviceName) {
|
||||
write-output "Variable not specified ScreenConnectService, please create a global custom field under Client called ScreenConnectService, Example Value: `"ScreenConnect Client (1327465grctq84yrtocq)`" `n"
|
||||
$ErrorCount += 1
|
||||
}
|
||||
|
||||
if (!$ErrorCount -eq 0) {
|
||||
exit 1
|
||||
}
|
||||
|
||||
$imagePath = Get-Itempropertyvalue "HKLM:\SYSTEM\ControlSet001\Services\$serviceName" -Name "ImagePath"
|
||||
$imagePath2 = ($imagePath -split "&s=")[1]
|
||||
$machineGUID = ($imagePath2 -split "&k=")[0]
|
||||
Write-Output $machineGUID
|
||||
@@ -1,18 +0,0 @@
|
||||
# Needs to be parameterized: $icofile, $URL, $ShortcutPath
|
||||
# Need to change paths and test/create if don't exist
|
||||
|
||||
|
||||
wget "https://www.example.com/logos/example.ico" -outfile "c:\agent\example.ico"
|
||||
|
||||
$WshShell = New-Object -comObject WScript.Shell
|
||||
$path = "C:\Users\All Users\desktop\example.url"
|
||||
$targetpath = "https://example.com/"
|
||||
$iconlocation = "c:\agent\example.ico"
|
||||
$iconfile = "IconFile=" + $iconlocation
|
||||
$Shortcut = $WshShell.CreateShortcut($path)
|
||||
$Shortcut.TargetPath = $targetpath
|
||||
$Shortcut.Save()
|
||||
Add-Content $path "HotKey=0"
|
||||
Add-Content $path "$iconfile"
|
||||
Add-Content $path "IconIndex=0"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
REM Print Spooler reset script. Will stop spooler, fix permissions on print folders, clear all files in print queues, and restart spooler service.
|
||||
|
||||
REM Stop Print Spooler
|
||||
net stop "Spooler"
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
net stop "Print Spooler"
|
||||
net start "Print Spooler"
|
||||
25
scripts_wip/Win_Print_Spooler_Restart_Service.ps1
Normal file
@@ -0,0 +1,25 @@
|
||||
<#
|
||||
.Synopsis
|
||||
Restart Print Spooler Service
|
||||
.DESCRIPTION
|
||||
Will force-restart the spooler service. With additional command parameter will also delete any pending print jobs
|
||||
.EXAMPLE
|
||||
Another example of how to use this cmdlet
|
||||
.OUTPUTS
|
||||
Any print jobs that are deleted
|
||||
.NOTES
|
||||
v1.0 5/2021
|
||||
https://github.com/silversword411
|
||||
.FUNCTIONALITY
|
||||
Print Spooler Troubleshooting, restarts spooler service. Can also delete all print jobs that are pending
|
||||
#>
|
||||
|
||||
#Restart Spooler service
|
||||
Restart-Service -Name spooler -Force
|
||||
|
||||
#Deletes All print jobs within the last 15 years
|
||||
$PrintJobs = get-wmiobject -class "Win32_PrintJob" -namespace "root\CIMV2" -computername . | Where-Object { [System.Management.ManagementDateTimeConverter]::ToDateTime($_.TimeSubmitted) -lt (Get-Date).AddDays(-5500) }
|
||||
foreach ($job in $PrintJobs) {
|
||||
# Write-Host "Canceling job $($job.JobId)"
|
||||
$job.Delete()
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#Update with command parameters
|
||||
#Deletes All print jobs within the last 2 days
|
||||
|
||||
$PrintJobs = get-wmiobject -class "Win32_PrintJob" -namespace "root\CIMV2" -computername . | Where-Object { [System.Management.ManagementDateTimeConverter]::ToDateTime($_.TimeSubmitted) -lt (Get-Date).AddDays(-2) }
|
||||
foreach ($job in $PrintJobs) {
|
||||
# Write-Host "Canceling job $($job.JobId)"
|
||||
$job.Delete()
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
md -Path 'C:\agent' -Force
|
||||
wget "http://www.yourwebsite.com/logos/yourico.ico" -outfile "c:\agent\yourico.ico"
|
||||
|
||||
|
||||
mkdir -Path 'C:\agent' -Force
|
||||
Invoke-WebRequest "http://www.yourwebsite.com/logos/yourico.ico" -outfile "c:\agent\yourico.ico"
|
||||
$WshShell = New-Object -comObject WScript.Shell
|
||||
$path = "C:\Users\All Users\desktop\Jaxsupport.url"
|
||||
$path = "C:\Users\All Users\desktop\Shortcut.url"
|
||||
$targetpath = "https://yourwebsite.com"
|
||||
$iconlocation = "c:\agent\yourico.ico"
|
||||
$iconfile = "IconFile=" + $iconlocation
|
||||
|
||||
16
update.sh
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
SCRIPT_VERSION="121"
|
||||
SCRIPT_VERSION="123"
|
||||
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/update.sh'
|
||||
LATEST_SETTINGS_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/api/tacticalrmm/tacticalrmm/settings.py'
|
||||
YELLOW='\033[1;33m'
|
||||
@@ -184,14 +184,14 @@ if ! [[ $HAS_PY39 ]]; then
|
||||
sudo rm -rf Python-3.9.2 Python-3.9.2.tgz
|
||||
fi
|
||||
|
||||
HAS_NATS220=$(/usr/local/bin/nats-server -version | grep v2.2.3)
|
||||
HAS_NATS220=$(/usr/local/bin/nats-server -version | grep v2.2.6)
|
||||
if ! [[ $HAS_NATS220 ]]; then
|
||||
printf >&2 "${GREEN}Updating nats to v2.2.3${NC}\n"
|
||||
printf >&2 "${GREEN}Updating nats to v2.2.6${NC}\n"
|
||||
nats_tmp=$(mktemp -d -t nats-XXXXXXXXXX)
|
||||
wget https://github.com/nats-io/nats-server/releases/download/v2.2.3/nats-server-v2.2.3-linux-amd64.tar.gz -P ${nats_tmp}
|
||||
tar -xzf ${nats_tmp}/nats-server-v2.2.3-linux-amd64.tar.gz -C ${nats_tmp}
|
||||
wget https://github.com/nats-io/nats-server/releases/download/v2.2.6/nats-server-v2.2.6-linux-amd64.tar.gz -P ${nats_tmp}
|
||||
tar -xzf ${nats_tmp}/nats-server-v2.2.6-linux-amd64.tar.gz -C ${nats_tmp}
|
||||
sudo rm -f /usr/local/bin/nats-server
|
||||
sudo mv ${nats_tmp}/nats-server-v2.2.3-linux-amd64/nats-server /usr/local/bin/
|
||||
sudo mv ${nats_tmp}/nats-server-v2.2.6-linux-amd64/nats-server /usr/local/bin/
|
||||
sudo chmod +x /usr/local/bin/nats-server
|
||||
sudo chown ${USER}:${USER} /usr/local/bin/nats-server
|
||||
rm -rf ${nats_tmp}
|
||||
@@ -307,5 +307,9 @@ if [[ "${CURRENT_MESH_VER}" != "${LATEST_MESH_VER}" ]] || [[ "$force" = true ]];
|
||||
sudo systemctl start meshcentral
|
||||
fi
|
||||
|
||||
# apply redis configuration
|
||||
sudo redis-cli config set appendonly yes
|
||||
sudo redis-cli config rewrite
|
||||
|
||||
rm -f $TMP_SETTINGS
|
||||
printf >&2 "${GREEN}Update finished!${NC}\n"
|
||||
1996
web/package-lock.json
generated
@@ -10,33 +10,27 @@
|
||||
"test:e2e:ci": "cross-env E2E_TEST=true start-test \"quasar dev\" http-get://localhost:8080 \"cypress run\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@quasar/extras": "^1.10.4",
|
||||
"@quasar/extras": "^1.10.6",
|
||||
"apexcharts": "^3.23.1",
|
||||
"axios": "^0.21.1",
|
||||
"dotenv": "^8.2.0",
|
||||
"prismjs": "^1.22.0",
|
||||
"qrcode.vue": "^1.7.0",
|
||||
"quasar": "^1.15.13",
|
||||
"quasar": "^1.15.20",
|
||||
"vue-apexcharts": "^1.6.0",
|
||||
"vue-prism-editor": "^1.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@quasar/app": "^2.2.6",
|
||||
"@quasar/cli": "^1.1.3",
|
||||
"core-js": "^3.11.2",
|
||||
"@quasar/app": "^2.2.10",
|
||||
"@quasar/cli": "^1.2.1",
|
||||
"core-js": "^3.14.0",
|
||||
"eslint-plugin-cypress": "^2.11.2",
|
||||
"flush-promises": "^1.0.2",
|
||||
"fs-extra": "^9.1.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"last 4 Chrome versions",
|
||||
"last 4 Firefox versions",
|
||||
"last 2 Edge versions",
|
||||
"last 3 Safari versions",
|
||||
"last 3 Android versions",
|
||||
"last 3 ChromeAndroid versions",
|
||||
"last 3 FirefoxAndroid versions",
|
||||
"last 3 iOS versions",
|
||||
"last 2 Opera versions"
|
||||
"last 3 Chrome versions",
|
||||
"last 3 Firefox versions",
|
||||
"last 2 Edge versions"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -261,6 +261,9 @@
|
||||
@click="eventLogMoreInfo(props.row)"
|
||||
>Last Output</span
|
||||
>
|
||||
<span v-else-if="props.row.check_type === 'diskspace' || props.row.check_type === 'winsvc'">{{
|
||||
props.row.more_info
|
||||
}}</span>
|
||||
</q-td>
|
||||
<q-td>{{ props.row.last_run || "Never" }}</q-td>
|
||||
<q-td v-if="props.row.assigned_task !== null && props.row.assigned_task.length > 1"
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
@input="value => $emit('input', value)"
|
||||
:rules="[...validationRules]"
|
||||
reactive-rules
|
||||
autogrow
|
||||
/>
|
||||
|
||||
<q-toggle
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
<q-item-section>Global Settings</q-item-section>
|
||||
</q-item>
|
||||
<!-- code sign -->
|
||||
<q-item v-if="!noCodeSigning" clickable v-close-popup @click="showCodeSign = true">
|
||||
<q-item v-if="!hosted" clickable v-close-popup @click="showCodeSign = true">
|
||||
<q-item-section>Code Signing</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
@@ -124,7 +124,7 @@
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
<!-- help -->
|
||||
<q-btn size="md" dense no-caps flat label="Help">
|
||||
<q-btn v-if="!hosted" size="md" dense no-caps flat label="Help">
|
||||
<q-menu auto-close>
|
||||
<q-list dense style="min-width: 100px">
|
||||
<q-item clickable v-close-popup @click="openHelp('docs')">
|
||||
@@ -272,8 +272,8 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
noCodeSigning() {
|
||||
return this.$store.state.noCodeSign;
|
||||
hosted() {
|
||||
return this.$store.state.hosted;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -136,7 +136,7 @@ export default {
|
||||
getOptions() {
|
||||
this.getClients();
|
||||
this.getSites();
|
||||
this.agentOptions = Object.freeze(this.getAgentOptions());
|
||||
this.agentOptions = this.getAgentOptions();
|
||||
},
|
||||
show() {
|
||||
this.$refs.dialog.show();
|
||||
@@ -152,7 +152,7 @@ export default {
|
||||
this.hide();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
created() {
|
||||
this.getOptions();
|
||||
|
||||
// copy prop data locally
|
||||
|
||||
@@ -143,7 +143,7 @@ export default {
|
||||
getOptions() {
|
||||
this.getClients();
|
||||
this.getSites();
|
||||
this.agentOptions = Object.freeze(this.getAgentOptions());
|
||||
this.agentOptions = this.getAgentOptions();
|
||||
},
|
||||
show() {
|
||||
this.$refs.dialog.show();
|
||||
@@ -159,7 +159,7 @@ export default {
|
||||
this.hide();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
created() {
|
||||
this.getOptions();
|
||||
|
||||
// copy prop data locally
|
||||
|
||||
@@ -111,6 +111,7 @@
|
||||
dense
|
||||
options-dense
|
||||
outlined
|
||||
clearable
|
||||
v-model="template.action"
|
||||
:options="scriptOptions"
|
||||
map-options
|
||||
@@ -174,6 +175,7 @@
|
||||
dense
|
||||
options-dense
|
||||
outlined
|
||||
clearable
|
||||
v-model="template.resolved_action"
|
||||
:options="scriptOptions"
|
||||
map-options
|
||||
@@ -696,4 +698,4 @@ export default {
|
||||
if (this.editing) Object.assign(this.template, this.alertTemplate);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
stack-label
|
||||
filled
|
||||
counter
|
||||
class="full-width"
|
||||
accept=".exe"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
|
||||
@@ -138,6 +138,7 @@
|
||||
v-model="localField.default_value_string"
|
||||
:rules="[...defaultValueRules]"
|
||||
reactive-rules
|
||||
autogrow
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
stack-label
|
||||
filled
|
||||
counter
|
||||
class="full-width"
|
||||
accept=".ps1, .bat, .py"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
|
||||
@@ -73,7 +73,12 @@
|
||||
label="Collector Task"
|
||||
v-model="collector"
|
||||
class="q-pb-sm"
|
||||
@input="autotask.custom_field = null"
|
||||
@input="
|
||||
() => {
|
||||
autotask.custom_field = null;
|
||||
autotask.collector_all_ouput = false;
|
||||
}
|
||||
"
|
||||
/>
|
||||
<q-select
|
||||
v-if="collector"
|
||||
@@ -87,6 +92,13 @@
|
||||
options-dense
|
||||
hint="The last line of script output will be saved to custom field selected"
|
||||
/>
|
||||
<q-checkbox
|
||||
v-if="collector"
|
||||
dense
|
||||
label="Save all output (Only for text area)"
|
||||
v-model="autotask.collector_all_output"
|
||||
class="q-py-sm"
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<q-select
|
||||
@@ -229,6 +241,7 @@ export default {
|
||||
task_type: "scheduled",
|
||||
timeout: 120,
|
||||
alert_severity: "info",
|
||||
collector_all_output: false,
|
||||
},
|
||||
policyChecks: [],
|
||||
severityOptions: [
|
||||
|
||||
@@ -90,6 +90,13 @@
|
||||
options-dense
|
||||
hint="The return value of script will be saved to custom field selected"
|
||||
/>
|
||||
<q-checkbox
|
||||
v-if="collector"
|
||||
dense
|
||||
label="Save all output (Only for text area)"
|
||||
v-model="autotask.collector_all_output"
|
||||
class="q-py-sm"
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<q-input
|
||||
@@ -131,6 +138,7 @@ export default {
|
||||
alert_severity: null,
|
||||
timeout: 120,
|
||||
custom_field: null,
|
||||
collector_all_output: false,
|
||||
},
|
||||
collector: false,
|
||||
customFieldOptions: [],
|
||||
@@ -197,6 +205,7 @@ export default {
|
||||
this.autotask.alert_severity = this.task.alert_severity;
|
||||
this.autotask.timeout = this.task.timeout;
|
||||
this.autotask.custom_field = this.task.custom_field;
|
||||
this.autotask.collector_all_output = this.task.collector_all_output;
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -72,18 +72,18 @@ export default {
|
||||
|
||||
isValidThreshold(warning, error, diskcheck = false) {
|
||||
if (warning === 0 && error === 0) {
|
||||
Notify.create(notifyErrorConfig("Warning Threshold or Error Threshold need to be set", 2000));
|
||||
return false
|
||||
Notify.create({ type: "negative", timeout: 2000, message: "Warning Threshold or Error Threshold need to be set" });
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!diskcheck && warning > error && warning > 0 && error > 0) {
|
||||
Notify.create(notifyErrorConfig("Warning Threshold must be less than Error Threshold", 2000));
|
||||
return false
|
||||
Notify.create({ type: "negative", timeout: 2000, message: "Warning Threshold must be less than Error Threshold" });
|
||||
return false;
|
||||
}
|
||||
|
||||
if (diskcheck && warning < error && warning > 0 && error > 0) {
|
||||
Notify.create(notifyErrorConfig("Warning Threshold must be more than Error Threshold", 2000));
|
||||
return false
|
||||
Notify.create({ type: "negative", timeout: 2000, message: "Warning Threshold must be more than Error Threshold" });
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -35,7 +35,7 @@ export default function () {
|
||||
defaultAgentTblTab: "server",
|
||||
clientTreeSort: "alphafail",
|
||||
clientTreeSplitter: 11,
|
||||
noCodeSign: false,
|
||||
hosted: false,
|
||||
},
|
||||
getters: {
|
||||
clientTreeSplitterModel(state) {
|
||||
@@ -159,8 +159,8 @@ export default function () {
|
||||
SET_CLIENT_TREE_SORT(state, val) {
|
||||
state.clientTreeSort = val
|
||||
},
|
||||
SET_NO_CODE_SIGN(state, val) {
|
||||
state.noCodeSign = val
|
||||
SET_HOSTED(state, val) {
|
||||
state.hosted = val
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
||||
@@ -742,7 +742,7 @@ export default {
|
||||
this.$store.commit("SET_AGENT_DBLCLICK_ACTION", r.data.dbl_click_action);
|
||||
this.$store.commit("SET_URL_ACTION", r.data.url_action);
|
||||
this.$store.commit("setShowCommunityScripts", r.data.show_community_scripts);
|
||||
this.$store.commit("SET_NO_CODE_SIGN", r.data.no_code_sign);
|
||||
this.$store.commit("SET_HOSTED", r.data.hosted);
|
||||
});
|
||||
},
|
||||
showToggleMaintenance(node) {
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
stack-label
|
||||
filled
|
||||
counter
|
||||
class="full-width"
|
||||
accept=".exe"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
|
||||