AzureAD priv preview

funkcje preview można niby włączyć… ale nie wszystkie. niektóre są dostępne – jeśli doda się specjalną flagę.

znalazłem poszukując narzędzia do weryfikacji uprawnień Enterprise Apps. obecnie jest spory śmietnik – apki pojawiają się i po pewnych czasie są trudności z określeniem co do czego ma uprawnienia (globalnie/centralnie).

po włączeniu tej funkcji dostępne  są dodatkowe opcje pozwalające na przejrzenie uprawnień wszystkich aplikacji – niestety brak opcji 'download’. ciekawą funkcją jest również application risk:

próbowałem znaleźć inne opcje, które dzięki tej fladze są dostępne, ale na razie nie trafiłem – zachęcam do poszukania i podzielenia się odkryciami.

eN.

katalog stricte sieciowy

w jaki sposób 'dostarczyć’ jakiś katalog na kilka serwerów/VDI? łatwym i popularnym sposobem jest wykorzystanie Azure Files i po prostu podmapować dysk sieciowy.

takie rozwiązanie ma (potencjalnie) kilka wad. potencjalnie – ponieważ wszystko zależy od założeń po co i dla kogo to robimy:

  • dodatkowa litera dysq
  • trzeba dodatkowe polisy GPO żeby ścieżka była zaufana i żeby system nie pluł się przy każdym uruchomieniu

chodziło o dostarczenie wspólnego repozytorium, dostępnego na wszystkich VDI. można taki efekt osiągnąć mapując udział sieciowy jako katalog.

potrzebny do tego jest Storage Account wspierający Azure Files, dodany do domeny. jest to konieczne ze względu na możliwość uwierzytelnienia. a kiedy mamy już założony Azure Files, wystarczy utworzyć w systemie katalog, który będzie na niego wskazywał:

PS> new-item -type SymbolicLink -path c:\myRepo -target \\wfilesrepo.file.core.windows.net\repo

as simple as that. i mamy katalog, który zachowuje się i wygląda (niemal) jak normalny katalog. nie trzeba już polis GPO ponieważ Windows już nie traktuje tego jako dysq sieciowego a zwykły katalog lokalny.

dodam do tego kilka uwag i ciekawostek:

  • taki symlink jest tylko wskazaniem. a to oznacza, że każdy oddzielnie musi mieć uprawnienia dostępu – to nie jest tak, że admin ma dostęp i podmapuje i każdy wlezie. trzeba więc odpowiednio posiedzieć nad uprawnieniami
  • standardowo uprawnieniami dostępu do Azure Files jest moduł IAM – to zresztą ogólna zasada. pojawia się tutaj problem nadania uprawnienia wszystkim – ponieważ nie nadamy na poziomie IAM uprawnienia dla 'authenticated users’. jest od jakiegoś czasu możliwość obejścia tego problemu, ustawiając atrybut ’defaultSharePermissions’ dla Storage Account.
  • ponieważ jest to link do sieci, jest problem z cache przy operacjach zapisu. zauważyłem, że czasem zapis jest odrzucany.
  • SymLink zachowuje się jak katalog… ale nie do końca. SymLink jako katalog odziedziczy uprawnienia z katalogu nadrzędnego, ale nawet jeśli jest włączona flaga dziedziczenia, nie do końca uprawnienia spływają poniżej. należy uważać i dobrze przetestować. najlepiej odpowiednio przygotować strukturę i wyłączyć dziedziczenie:
    \\wfilesrepo.file.core.windows.net\repo\wlasciwykatalog – i wyłączyć dziedziczenie na poziomie 'wlasiciwykatalog’
    albo
    c:\root\[repo] – gdzie [repo] to symlink – i wyłączyć dziedziczenie na poziomie 'root’…
    to problemu nie będzie. tylko jest wtedy bzdurny dodatkowy katalog /:
  • dodatkowy problem z uprawnieniami jaki zauważyłem, to fakt, że uprawnienia nadane dla grupy 'administrators’ bezpośrednio na file share, nie działają O_o . ogólnie: UWAGA NA UPRAWNIENIA, bo zachowują się dziwnie.
  • i jeszcze – jak takiego symlinka usunąć? najlepiej:
(get-item c:\symlink).delete()

mimo pewnych dziwnych zachowań sprawdza się fajnie a dla użytkowników wygląda i zachowuje się jak folder lokalny.

eN.

qrs PowerShell niedostępny

ponieważ dostaję trochę maili i wiadomości, gwoli wyjaśnienia:

jakiś czas temu przygotowałem szkolenie z PowerShell które było dostępne na MVA (Microsoft Virtual Academy). MVA umało… ale materiały dla MVA były de facto trzymane na channel9. tam można było do niedawna je obejrzeć… ale channel9 również umarło i niestety większość materiałów wraz z nim. niestety nie mam kopii – poza tym było robione na zlecenie Microsoft, więc i tak byłby problem żebym je opublikował…

mam gdzieś w tyle głowy, żeby zacząć nagrywać samodzielnie, ale … życie. mam nadzieję, że uda mi się znaleźć motywację na wykrojenie jakiegoś czasu i nagranie go na nowo… 'jak tylko się ogarnę z projektami’ (;

eN.

anti-idle

o co cho?

pracując na kilq terminalach mam często problem z blokadą ekranu i wymuszeniem hasła – przez agresywne GPO. wpisywanie hasła za każdym razem zabiera czas – zwłaszcza jak tych sesji jest otwartych kilka i trzeba się do nich dostać.

wystarczy ruszyć myszką albo nacisnąć klawisz, żeby idle-timer się wyzerował. oba te zdarzenia można wywołać ze skryptu, a więc – jest rozwiązanie q:

skrypt

gotowy skrypt umieściłem na GH.

jest również wersja skompilowana za pomocą PS2EXE.

eN.

w-files w audio

blog od dłuższego czasu przymiera… jeśli chcesz się dowiedzieć czemu…

ostatnio miałem przyjemność gościć w Drafcie Rozmowy więc mogę uznać, że to taki wpis w wersji audio… trochę nawiązując do (niedoszłego) tematu rozmowy – zauważalny jest trend przejścia ze słowa pisanego do multimediów … ale o tym będzie następnym razem. tym razem rozmawialiśmy o tym, co robimy na codzień (i kochamy) – czyli o IT.

podcast (ten i wiele innych!) dostępny na wielu platformach, ja pozwolę sobie podlinkować ten googlowy, ale znajdziecie w swoim ulubionym playerze pod hasłem 'draft rozmowy’ (:

eN.

Passwordless dla Microsoft Account

niedawno Microsoft ogłosił funkcję 'passwordless’ dla kont 'Microsoft Account’ – czyli tych nie-firmowych/nie-szkolnych. wszystkie arty dot. braq haseł rozpływają się w zaletach tego rozwiązania i ah, oh [fapfap] … więc daruję sobie powtarzanie zalet braq hasła. funkcję włączyłem i … obniżyłem sobie bezpieczeństwo dostępu.

trzeba jasno odgrodzić passwordless dla firm i funkcje Windows Hello for Business, który zakłada MultiFactor Auth – z naciskiem na ’Mutli’, od passwordless dla kont MS. zastanawiałem się jak to będzie praktycznie zrealizowane, włączyłem –  i osobiście mnie to trochę przeraża – ponieważ z MFA wracamy do Single Factor Auth, którym jest nasz smartfon. nie spodziewam się, aby wiele osób miało na kontach dane warte ucięcia komuś kciuka [i przechowywania w odpowiednich warunkach], ale mimo wszystko logowanie, w którym JEDYNYM czynnikiem jest kliknięcie na ekranie telefonu, budzi u mnie alarm. jakiś czas temu, o ile pamiętam, była opcja dodatkowego zabezpieczenia apki Microsoft Authenticator PINem. dziś znajduję w opcjach jedynie 'require biometric or PIN’ ale bez możliwości wyboru – wygląda na to, że ustawienie pobierane jest z systemu (sprawdzałem na Android) – a więc czy będzie to odcisk, czy PIN – będzie to TEN SAM odcisk/PIN, co do reszty. ciekawy jestem czy ta formuła faktycznie zadziała – dowodem będzie, że przez najbliższe kilka lat będzie funkcjonować w tej postaci, będzie używana, a w prasie nie zaczną się pojawiać arty o tym jak to wypłynęła porcja danych/zdjęć/doqmentów bo ktoś zgubił smartfona. imho bardzo szybko pojawi się nowa wersja apki lub ustawienie, które doda jakiś drugi czynnik – może PIN, może kod SMS, czy choćby wybór obrazka. co ciekawe w ustawieniach konta nadal widzę 'Two-step auth ON’ – pomimo, że loguję się teraz tylko przykładając palec do ekranu.

niezależnie od mojej subiektywnej opinii, każdy powinien zrewidować swoje ustawienia haseł i upewnić się:

  • czy na pewno mamy skonfigurowane dodatkowe metody uwierzytelnienia?
  • czy je pamiętamy? [np. czy znamy odpowiedzi do pytań weryfikacyjnych]
  • czy ustawienia tych metod nie zapętlają się?

jeśli naszą drugą metodą jest adres email, który ma włączone MFA oparte o tą samą apkę – to tracąc telefon nie zalogujemy się nigdzie. powinniśmy mieć realną alternatywę inaczej czeka nas długie przebijanie się przez support. jeśli coś takiego wydarzy się gdy nie mamy czasu, będziemy w kropce. każdy powinien poświęcić czas chociaż raz, i 'na niby’ zgubić telefon. wyłączyć go i sprawdzić gdzie jest w stanie się zalogować, czego braqje, ile to trwa czasu. w firmach na podobne scenariusze powinny istnieć procedury, ale sprawę ułatwia, że zazwyczaj jest kilq administratorów, więc w większości scenariuszy są sobie w stanie pomóc na wzajem. prywatnie, nas czeka przebijanie się przez linie wsparcia – warto przynajmniej spróbować, żeby oszacować ile to może trwać czasu i jak w ogóle taka procedura wygląda.

z jednej strony firmy dwoją się i troją, żeby było bezpieczniej a zarazem łatwiej… wygoda na pewno się zwiększa – ale w momencie kiedy coś się sypnie, zaczynają się schody. i wcale nie jest łatwo wymyśleć i skonfigurować swoje konta tak, aby faktycznie odzyskać awaryjnie dostęp szybko i bezpiecznie.

ciekaw jestem opinii – bo na razie nie trafiłem na te negatywne czy krytyczne. moja jest dość mieszana – niby wygodnie, ale [w przypadku kont prywatnych] na pewno nie bezpieczniej!

eN.

 

get-member – jak używać?

potęgą PowerShell jest jego wszech-obiektowość. zrozumienie pracy na takich obiektach to połowa sukcesu. dlatego kiedy coś nie działa lub zachowuje się inaczej niż spodziewanie – używa się get-member [lub po prostu gm]. drugim podstawowym mechanizmem jest oczywiście potokowanie – pipelining. i tak najprostszym sposobem sprawdzenia 'co kryje w sobie obiekt’ jest wykonanie

$something|gm

jest jednak pewien … no właśnie – niuans? niby niuans ale de facto istota funkcjonowania mechanizmów PS związany z tym, czym tak na prawdę jest potokowanie. prosty przykład:

$array=@(1,2)
$array|gm

czego intuicyjnie będziesz oczekiwał(a)? czy get-member zwróci informacje o zmiennej typu 'tablica’? oczywiście nie – zwróci opis zmiennych integer:

TypeName: System.Int32

Name MemberType Definition
---- ---------- ----------
CompareTo Method int CompareTo(System.Object value), int CompareTo(int value), int IComparable.CompareTo(System.Object obj), int IComparable[int].Comp...
Equals Method bool Equals(System.Object obj), bool Equals(int obj), bool IEquatable[int].Equals(int other)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
[...]

to zapewne nie jest zaskoczenie, ponieważ każdy zna to z praktyki. jak się nad tym jednak zastanowić, to nie jest oczywiste – czemu w wyniq dostaje się wynik get-member dla elementów tablicy a nie samej zmiennej tablicowej? ponieważ tak właśnie działa potok [pipe]. de facto jest to skrótowa wersja takiegoż zapisu w klasycznych językach:

foreach ($element in $array) {
  get-member -inputObject $element
}

i teraz wszystko jasne! tak bardzo uzależniamy się od automatyki PS i używania potoków, że czasem można zapomnieć, że to nie jest ani jedyny sposób użycia, ani – jak widać na tym przykładzie – niekoniecznie prawidłowy. jeśli więc chcemy zbadać konkretnie obiekt tablicowy, a nie jego elementy, wystarczy zaniechać potokowania:

get-member -inputObject $array

TypeName: System.Object[]

Name MemberType Definition
---- ---------- ----------
Count AliasProperty Count = Length
Add Method int IList.Add(System.Object value)
Address Method System.Object&, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Address(int )
Clear Method void IList.Clear()
Clone Method System.Object Clone(), System.Object ICloneable.Clone()
CompareTo Method int IStructuralComparable.CompareTo(System.Object other, System.Collections.IComparer comparer)
Contains Method bool IList.Contains(System.Object value)
CopyTo Method void CopyTo(array array, int index), void CopyTo(array array, long index), void ICollection.CopyTo(array array, int index)
[...]

eN.

 

PS Borderlands: $null.. czy nie?

$null czy nie $null, o to jest pytanie

jak sprawdzić czy zmienna została prawidłowo zainicjowana czy może jest wyzerowana? sposobów jest oczywiście kilka, zależnie od kontekstu. ale ponieważ trafiłem na brzegową sytuację, kilka ciekawostek…

pierwsze pytanie – co to znaczy 'wyzerowana zmienna’? od razu przychodzi mi do głowy pytanie-killer, dobre na sprawdzenie sprytu początqjącego deva – 'ile wartości można zakodować na jedno-bitowej zmiennej?’. dość oczywistą odpowiedzią jest 'dwie – 0 i 1′, ale prawidłową jest 'trzy – zero, jeden i null’. [oczywiście przykład wyssany z palca i można z tym polemizować, ale chodzi o ideę]. pokazuje to pewien niuans w sprawdzaniu wartości zmiennej, który może nieźle namieszać – zmienna pusta [empty] to nie to samo co zainicjowana zmienna z wartością $null [o ile $null można nazwać wartością, ale nie chodzi o filozofowanie tylko praktyczne konsekwencje].

początkowo zatem używałem prostego porównania:

if($zmienna -eq $null) { }

potem dowiedziałem się, że $null powinien być zawsze po lewej stronie, ponieważ w specyficznych przypadkach zachowa się 'nieoczekiwanie’ – polecam poczytać na docsach. dla tego poprawnym zapisem jest:

if($null -eq $zmienna) {}

przeważnie jednak, ze względu na logikę działania skryptów, chcę sprawdzić czy coś nie jest $null LUB puste. zacząłem więc przez długi czas używać magii PowerShell:

if(-not $zmienna) { }

co w zasadzie powinno uniwersalnie sprawdzić zmienną. tego zapisu nie używam od dłuższego czasu – nie pamiętam jednak dokładnie jakie tutaj występowały aberracje, ale z jakiegoś powodu przesiadłem się na natywną funkcję .NET dla klasy string:

[string]::isNullOrEmpty($zmienna)

i bardzo długo tą funkcję uważałem za uniwersalny tester i myślałem że znalazłem moje panaceum… do czasu aż nie trafiłem na kolejne brzegowe sytuacje, które nieoczekiwanie zwracają głupotę. najpierw przykład oczywisty, tutaj nie ma co się dziwić:

PS> $emptyArray=@()
PS> $emptyArray.count
0
PS> [string]::isNullOrEmpty($emptyArray)
True

kolejny nadal dość oczywisty przykład, choć warto zwrócić uwagę na olbrzymią różnię pomiędzy 'tablicą pustą’ z poprzedniego przykładu, a tablicą zawierającą element $null – zwróć uwagę na ilość elementów w tablicy:

PS> $nullArray=@($null)
PS> $nullArray.count
1
PS> [string]::isNullOrEmpty($nullArray)
True

i tu doszedłem do PS Borderlands – granic swojej wiedzy i możliwości zrozumienia mechanizmów pod spodem. dalszych przykładów nie potrafię wyjaśnić – znam je z obserwacji:

PS> $nullArray=@($null,$null)
PS> $nullArray.count
2
PS> [string]::isNullOrEmpty($nullArray)
False

wychodzi na to że jedno nic to nadal nic, ale dwa nic to już coś. miałem na studiach matematykę nieskończoności, ale nigdy matematyki nicości, najwyraźniej muszę tą dziedzinę lepiej zgłębić (;

a dalej… to już kosmos. przypadek tak zawikłany, że zajęło mi masę czasu póki go wyizolowałem i umiałem pokazać na przykładzie. w przeszłości również trafiałem na podobne zachowanie, więc scenariuszy jest prawdopodobnie więcej, ale dopiero teraz postanowiłem to zrozumieć. opiszę, może znajdzie się harpagan, który to rozwikła…

poza granicami

jeśli funkcja implementuje 'ValueFromRemainingArguments’, typ zwracanego argumentu będzie zależny od tego czy się z tej funkcjonalności skorzysta. tak działa między innymi 'write-host’ – wywołanie polecenia:

PS> write-host tu sobie cos tam napiszemy bez zadnych cudzyslowiow
tu sobie cos tam napiszemy bez zadnych cudzyslowiow

bez cudzysłowiów – czyli de fakto polecenie powinno potraktować każdy wyraz jako oddzielą wartość kolejnego parametru. a jednak zinterpretował to jako pojedynczą wiadomość. to właśnie instrukcja ValueFromRemainingArguments upakowała wszystkie nienazwane wartości pod jedną zmienną. podobny myk stosuję w swoim write-log dzięki czemu mogę uzyskać podobny efekt. na tej też funkcji mogłem zbadać, że przy takim wywołaniu:

PS> write-log -message "to cos"
to cos

zmienna $message będzie typu [string]. ale przy takich wywołaniach:

PS> write-log "to cos"
to cos
PS> write-log to inne cos
to inne cos

zmienna $message jest typu [`list] [nie pytajcie skąd ten backtick w nazwie typu – bardzo dziwaczny to zapis, dodatkowa przyprawa w tym i tak dziwnym przypadq]. to pierwsza ważna różnica, ponieważ problem pojawia się właśnie przy tym typie. a że zazwyczaj ułatwiamy sobie życie i jeśli można ’-message’ nie pisać, to go nie piszemy, scenariusz więc dość codzienny.

jeśli jako wiadomość przekaże się obiekt, to zostanie stworzona lista zawierająca obiekty. można to zasymulować tak:

PS> $przekazaneWartosci = new-object System.Collections.Generic.List[system.object]
PS> $mojObiekt = [PSCustomObject] @{page='w-files.pl';proto='https'}
PS> $mojObiekt

page proto
---- -----
w-files.pl https

PS> $przekazaneWartosci.add($mojObiekt)
PS> [string]::isNullOrEmpty($przekazaneWartosci)
True

jeśli obiekt wrzucony do listy jest typu PSCustomObject – nawet jeśli nie jest pusty, funkcja zwraca informację, że lista jest pusta. można by powiedzieć – 'no ale panie, to jest metoda dla stringa, a tutaj masz obiekt, to pewnie dlatego’. tyle, że sprawdzałem na wielu innych typach obiektów – i zachowuje się prawidłowo. póki co ten bug wydaje się działać tylko dla PSCustomObject (-> update dalej). już nawet taka zmiana w deklaracji, z custom na generic:

$myobj = [PSObject] @{page='w-files.pl';proto='https'}

spowoduje, że będzie działać prawidłowo. różnica pomiędzy PSObject a PSCustomObject mogłoby być też fajnym wpisem PS Borderlands i straciłem kiedyś sporo czasu na braq spostrzegawczości, że jest inna deklaracja… ale to nie teraz. dodatkowego smaczq dodaje fakt, że nie do końca wiadomo, kiedy coś zostanie przetworzone na PSCustomObject. ot proszę przykład:

PS> (get-process).gettype()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array

PS> (get-process|select processname).gettype()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array

PS> $ou='OU=borderlands,DC=w-files,DC=pl'
PS> (Get-ADOrganizationalUnit -Identity $ou).gettype()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False ADOrganizationalUnit Microsoft.ActiveDirectory.Management.ADObject

PS C:\CloudTeam-ScriptLAB> (Get-ADOrganizationalUnit -Identity $ou|select name).gettype()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False PSCustomObject System.Object

z jakiegoś powodu filtrowanie niektórych typów obiektów zmienia ich typ – kolejna zagadka, już poza granicami zrozumienia, ponieważ to nie jest normalne zachowanie.

update

dopiero co skończyłem ten wpis i zabrałem się za kolejny skrypt. kolejny przykład obiektu, dla którego [string]::isNullOrEmpty zwraca głupotę – i to dużo prostszy. wiedziałem, że trafiałem na więcej takich przypadqw!

PS> $storageAccountList = get-AzStorageAccount -resourceGroupName 'myRG'
PS> $storageAccountList

StorageAccountName ResourceGroupName PrimaryLocation SkuName Kind AccessTier CreationTime Provisioning State
------------------ ----------------- --------------- ------- ---- ---------- ------------ ------------
nexortestsa myRG westeurope Standard_LRS StorageV2 Hot 07.05.2021 10:56:16 Succeeded

PS> $storageAccountList.GetType()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False PSStorageAccount System.Object

PS> [string]::IsNullOrEmpty($storageAccountList)
True

podsumowanie

cała informatyka opiera się na determiniźmie [no w każdym razie do czasu pojawienia się AI/ML, które jest w IT odpowiednikiem fizyki kwantowej w fizyce (; ]. brak determinizmu powoduje, że trzeba się nieźle napocić żeby wyłapać błędy i prawidłowo je obsłużyć.

strasznie mnie drażni, kiedy nie potrafię czegoś zrozumieć. takie 'niuanse’ to jakieś górne wartości zasady pareto – ten promil promila całego systemu, na który trzeba poświęcić mnóstwo, mnóstwo czasu, żeby w ogóle go uchwycić. nie wspominając o zrozumieniu. w tym przypadq – to jest kilka brzegowych sytuacji, które nakładają się na siebie. istna qmulacja. IMHO to nie jest coś 'do zrozumienia’ a bardziej błędy w implementacji, ale póki co – wpada do teczki w-Files.

eN.

 

jak pobrać wartość zmiennej z wyższego zakresu

praktyka

zmienne mają swoje zakresy (scopes). zakresy są niby bardzo proste… ale czy na pewno? to jak pobrać zmienną z zakresu nadrzędnego (parent scope)?

bardzo proste:

get-variable -name <varName> -scope 1

teoria

ale, że '1′? zawsze i niezmiennie '1′? jak to tak? a jak pobrać wartość zakresu w którym obecnie się znajduję?


zmienne układane są na stosie, zwanym czasem kolejką LIFO (Last-In, First-Out). [najstarsza zarejestrowana implementacja LIFO to ’pierwsi będą ostatnimi, a ostatni pierwszymi’ więc łatwo zapamiętać (; ]. element na szczycie stosu, to element 'obecny’, i zawsze ma wartość '0′. proste, ale konsekwencje są nieoczywiste. do tego dochodzi specyfika samego PS i tego, jaki próbuje być 'przyjazny’, co dodatkowo może mieć niespodziewane konsekwencje. oprócz zakresów 'numerowanych’ są zakresy 'nazwane’ i to one powodują sporo zamieszania. nie będę kopiował definicji, które można znaleźć łatwo i szybko, ale z nazwy, podstawowe zakresy to 'Global’ i 'Script’. tak więc uruchamiając dowolny skrypt, zakresy wyglądają tak:

prosty kod testowy:

function level1 {
    $x = 1
    $x
}
$x = 0
level1
$x

wyświetli oczywiście '1′ a następnie '0′ – ponieważ przy każdym wyświetleniu $x wskazuje na zmienną z obecnego zakresu. obecnego, czyli '0′, co obrazuje:

mamy więc odpowiedź 'jak pobrać obecny zakres’ – jest to po prostu zakres zero. skompliqjmy ten skrypt tak, aby przetestować czy faktycznie 'parent scope’ to '1′:

function level1 {
    $x = 1
    "here: $x"
    "parent from l1: {0}" -f (Get-Variable -Name x -Scope 1).Value
    level2
}

function level2 {
    $x = 2
    "here: $x"
    "parent to l2: {0}" -f (get-variable -name x -Scope 1).value
    "script from l2: {0}" -f ($script:x)
}

$x=0
Write-Host -ForegroundColor Magenta "level1:"
level1

Write-Host -ForegroundColor Red "level2:"
level2

write-host -ForegroundColor Green "script:"
$script:x

write-host -ForegroundColor Yellow 'global:'
$Global:x

stos zmiennych dla tego wywołania ma teraz taką strukturę:

oczywiste? ruchome wartości dla zakresów nazwanych mogą sprawić problem i wydawać się nieintuicyjne. ale to nie koniec pułapek. ostatnia wartość [$global:x] może przyjąć albo $null albo… zero. zależnie od środowiska uruchomieniowego [możesz odpalić kod w VSC]. dodatkowo problemy może sprawić zmiana wartości zmiennej nadrzędnej… o ile odczytana zostanie zmienna z dowolnego, pierwszego zakresu, w którym istnieje, o tyle zmiana wartości będzie wykonana w tym samym zakresie. żeby to zobrazować, zmodyfiqjmy odrobinę pierwszy kod:

function level1 {
    $x
    $x = 1
}

$x = 0
level1
$x

tym razem na ekranie pojawią się dwa zera. w funkcji 'level1′ żądamy wyświetlenia $x, które w tym zakresie jeszcze nie istnienie. w związq z tym wyświetlony został $x z zakresu wyżej. ale w momencie wykonania „$x = 1”, zgodnie z definicją, iż przypisanie następuje zawsze w tym samym zakresie, zmienna została zainicjowana lokalnie – a nie nadpisana pierwsza istniejąca – i to jej przypisana została wartość '1′. a więc $x wyświtlony na końcu nie uległ zmianie i wyświelone zostało '0 0′

jeśli chcemy zmienić wartość zmiennej w zakresie nadrzędnym (parent scope) kod wygląda tak:

function level1 {
    $x
    set-variable -name x -scope 1 -Value 1
}

$x = 0
level1
$x

…no i warto zwrócić uwagę, że podając nazwy zmiennych dla commandletów *-variable nie dodaje się '$’.

…i może jeszcze to, że nie ma standardowo metody aby odpytać się o to, jak głęboki jest obecnie stos. jeśli masz taki scenariusz, to przyda ci się jeden z poprzednich wpisów. no dobra, podpowiem… to będzie „((get-PSCallStack).count-1)”.

eN.